diff --git a/.nvmrc b/.nvmrc index d581945ce..7f976a5ae 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -12.9.0 +12.12.0 diff --git a/package.json b/package.json index ba17a302d..e3d87beef 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "clean": "git clean -dfx", "publish": "npx lerna publish -m \"[skip travis] chore(release): publish %s\"", "postpublish": "auto release", - "preview:hbs": "cd packages/development-edition-engine-handlebars && npx patternlab add --starterkits '@pattern-lab/starterkit-handlebars-vanilla' && npm run pl:build" + "preview:hbs": "cd packages/development-edition-engine-handlebars && npx patternlab add --starterkits @pattern-lab/starterkit-handlebars-vanilla && npm run pl:build" }, "nyc": { "exclude": [ diff --git a/packages/core/src/index.js b/packages/core/src/index.js index b594fa5ee..1c575c3ab 100644 --- a/packages/core/src/index.js +++ b/packages/core/src/index.js @@ -77,6 +77,30 @@ const patternlab_module = function(config) { return patternlab.getVersion(); }, + /** + * Returns the current pattern lab configuration being used + * + * @memberof patternlab + * @name getConfig + * @instance + * @returns {object} the current patternlab-node config (defaults + customizations) + */ + getConfig() { + return config; + }, + + /** + * Returns if Pattern Lab is busy compiling or not + * + * @memberof patternlab + * @name isBusy + * @instance + * @returns {boolean} if pattern lab is currently busy compiling + */ + isBusy: function() { + return patternlab.isBusy; + }, + /** * Builds patterns, copies assets, and constructs user interface * diff --git a/packages/core/src/lib/patternlab.js b/packages/core/src/lib/patternlab.js index 4bacc8fe2..cd89c64c4 100644 --- a/packages/core/src/lib/patternlab.js +++ b/packages/core/src/lib/patternlab.js @@ -47,6 +47,7 @@ module.exports = class PatternLab { // Load up engines please this.engines = patternEngines; this.engines.loadAllEngines(config); + this.isBusy = false; // // INITIALIZE EMPTY GLOBAL DATA STRUCTURES diff --git a/packages/core/src/lib/watchPatternLabFiles.js b/packages/core/src/lib/watchPatternLabFiles.js index 2e5bfe09c..f5ab0c1aa 100644 --- a/packages/core/src/lib/watchPatternLabFiles.js +++ b/packages/core/src/lib/watchPatternLabFiles.js @@ -21,11 +21,11 @@ const watchPatternLabFiles = ( assetDirectories.source.meta, ]; const globalPaths = globalSources.map(globalSource => - path.join(basePath, globalSource, '*') + path.join(path.resolve(basePath, globalSource), '*') ); _.each(globalPaths, globalPath => { - logger.debug(`Pattern Lab is watching ${globalPath} for changes`); + logger.debug(`Pattern Lab is watching ${globalPath} for changes!`); if (patternlab.watchers[globalPath]) { patternlab.watchers[globalPath].close(); @@ -80,13 +80,14 @@ const watchPatternLabFiles = ( .concat(patternlab.engines.getSupportedFileExtensions()) .map(dotExtension => path.join( - basePath, - assetDirectories.source.patterns, + path.resolve(basePath, assetDirectories.source.patterns), `/**/*${dotExtension}` ) ); _.each(patternWatches, patternWatchPath => { - logger.debug(`Pattern Lab is watching ${patternWatchPath} for changes`); + logger.debug( + `Pattern Lab is watching ${patternWatchPath} for changes - local!` + ); if (patternlab.watchers[patternWatchPath]) { patternlab.watchers[patternWatchPath].close(); diff --git a/packages/development-edition-engine-handlebars/.nvmrc b/packages/development-edition-engine-handlebars/.nvmrc index 95c4e8d27..7f976a5ae 100644 --- a/packages/development-edition-engine-handlebars/.nvmrc +++ b/packages/development-edition-engine-handlebars/.nvmrc @@ -1 +1 @@ -10.0.0 \ No newline at end of file +12.12.0 diff --git a/packages/development-edition-engine-handlebars/README.md b/packages/development-edition-engine-handlebars/README.md index 722416c2a..5bb88ff96 100644 --- a/packages/development-edition-engine-handlebars/README.md +++ b/packages/development-edition-engine-handlebars/README.md @@ -10,3 +10,19 @@ This Development Edition is a variant of [Edition Node](https://github.com/patte * Build and test against Handlebars pattern tree > Development Editions of Pattern Lab provide the ability to work on and commit changes to select packages within the overall Pattern Lab [ecosystem](http://patternlab.io/docs/advanced-ecosystem-overview.html). This Edition is NOT stable. + + +## Working on Pattern Lab's UI Locally + +### Step 1: Install Dependencies +Run the following in the root of the Pattern Lab repo: + +``` +yarn run setup +``` + +### Step 2 (Optional) +If you want to build using a fuller set of examples than what comes with this default Handlebars demo, run `yarn run preview:hbs`. Otherwise skip to step 3. + +### Step 3 +Finally, go back into this folder, `cd packages/development-edition-engine-handlebars`, and start up the local dev server which watches UIKit and the local Pattern Lab instance for changes, live reloads, etc by running `yarn dev` diff --git a/packages/development-edition-engine-handlebars/package.json b/packages/development-edition-engine-handlebars/package.json index dc9cc720b..30518b13d 100644 --- a/packages/development-edition-engine-handlebars/package.json +++ b/packages/development-edition-engine-handlebars/package.json @@ -8,7 +8,8 @@ "pl:help": "patternlab --help", "pl:install": "patternlab install --config ./patternlab-config.json", "pl:serve": "patternlab serve --config ./patternlab-config.json", - "pl:version": "patternlab --version" + "pl:version": "patternlab --version", + "dev": "node ./node_modules/@pattern-lab/uikit-workshop/build-tools.js" }, "keywords": [ "Pattern Lab", @@ -24,7 +25,7 @@ "url": "git://github.com/pattern-lab/patternlab-node.git" }, "engines": { - "node": ">=10.0" + "node": ">=12.12.0" }, "dependencies": { "@pattern-lab/cli": "^5.3.0", diff --git a/packages/edition-twig/README.md b/packages/edition-twig/README.md new file mode 100644 index 000000000..2d8d9f3d0 --- /dev/null +++ b/packages/edition-twig/README.md @@ -0,0 +1,11 @@ +## Working on Pattern Lab's UI Locally + +### Step 1: Install Dependencies +Run the following in the root of the Pattern Lab repo: + +``` +yarn run setup +``` + +### Step 2 +Finally, go back into this folder, `cd packages/edition-twig`, and start up the local dev server which watches UIKit and the local Pattern Lab instance for changes, live reloads, etc by running `yarn dev` diff --git a/packages/edition-twig/package.json b/packages/edition-twig/package.json index 3cc6861d9..efe3f57d5 100644 --- a/packages/edition-twig/package.json +++ b/packages/edition-twig/package.json @@ -19,7 +19,8 @@ "install": "patternlab install --config ./patternlab-config.json", "serve": "patternlab serve --config ./patternlab-config.json", "start": "npm run serve", - "version": "patternlab --version" + "version": "patternlab --version", + "dev": "node ./node_modules/@pattern-lab/uikit-workshop/build-tools.js" }, "dependencies": { "@pattern-lab/cli": "^5.3.0", diff --git a/packages/uikit-workshop/.gitignore b/packages/uikit-workshop/.gitignore index 2b65dab9b..a3717f18d 100644 --- a/packages/uikit-workshop/.gitignore +++ b/packages/uikit-workshop/.gitignore @@ -4,3 +4,6 @@ src/bower_components/* .DS_Store /.eslintignore dist +public +www +dependencyGraph.json diff --git a/packages/uikit-workshop/.nvmrc b/packages/uikit-workshop/.nvmrc index 95c4e8d27..7f976a5ae 100644 --- a/packages/uikit-workshop/.nvmrc +++ b/packages/uikit-workshop/.nvmrc @@ -1 +1 @@ -10.0.0 \ No newline at end of file +12.12.0 diff --git a/packages/uikit-workshop/README.md b/packages/uikit-workshop/README.md index 76e5543ce..493568b52 100644 --- a/packages/uikit-workshop/README.md +++ b/packages/uikit-workshop/README.md @@ -27,3 +27,24 @@ In order to modify these assets you need to install the following: ## Development Set-up Read the [contribution guidelines](https://github.com/pattern-lab/patternlab-node/blob/master/packages/uikit-workshop/.github/CONTRIBUTING.md) + + +## Working on Pattern Lab's UI Locally + +### Step 1: Install Dependencies +Run the following in the root of the Pattern Lab repo: + +``` +yarn run setup +``` + +### Step 2 (Optional) +If you want to build using a fuller set of examples than what comes with the default Handlebars demo, run `yarn run preview:hbs`. Otherwise skip to step 3. + +### Step 3 +Finally, go into the `packages/development-edition-engine-handlebars` or `packages/edition-twig` folder and start up the local dev server which watches UIKit and the local Pattern Lab instance for changes, live reloads, etc by running `yarn dev` + +``` +cd packages/development-edition-engine-handlebars +yarn dev +``` diff --git a/packages/uikit-workshop/build-tools.js b/packages/uikit-workshop/build-tools.js new file mode 100644 index 000000000..5c6d7c3b3 --- /dev/null +++ b/packages/uikit-workshop/build-tools.js @@ -0,0 +1,16 @@ +const webpackServer = require('@pattern-lab/uikit-workshop/build/webpack-server.js'); +const path = require('path'); +const configFilePath = path.join(process.cwd(), './patternlab-config.json'); +const config = require(configFilePath); +const patternlab = require('@pattern-lab/core')(config); + +// build + start watch mode +patternlab.build({ + watch: true, + cleanPublic: true, +}); + +webpackServer.serve( + patternlab, + path.resolve(process.cwd(), config.paths.public.root) +); diff --git a/packages/uikit-workshop/build/webpack-dev-server-waitpage/index.js b/packages/uikit-workshop/build/webpack-dev-server-waitpage/index.js new file mode 100644 index 000000000..c24f694f1 --- /dev/null +++ b/packages/uikit-workshop/build/webpack-dev-server-waitpage/index.js @@ -0,0 +1,88 @@ +const fs = require('fs'); +const path = require('path'); +const ejs = require('ejs'); +const webpack = require('webpack'); + +const data = { + webpackVersion: webpack.version, + webpackDevServerVersion: '2.4.1', + progress: [[0]], +}; + +/** + * @typedef {object} WebpackDevServerWaitpageOptions + * @property title {string} + * @property theme {string} + * @property template {string} + * @property disableWhenValid {boolean} + */ + +/** @type {WebpackDevServerWaitpageOptions} */ +const defaultOptions = { + title: 'Development Server', + theme: 'pl-loading', + disableWhenValid: true, +}; + +/** + * webpack-dev-server-waitpage middleware factory + * @param server {Server} The server argument passed to webpack-dev-server's 'before' function + * @param [options] {WebpackDevServerWaitpageOptions} An optional object of options (see Readme for more information) + * @returns {Function} Koa compatible middleware + */ +const webpackDevServerWaitpage = (server, options) => { + if (!server) + throw new Error( + `webpack-dev-server's compilers argument must be supplied as first parameter.` + ); + + /** @type {WebpackDevServerWaitpageOptions} */ + options = Object.assign({}, defaultOptions, options); + + const compilers = server.compilers; + // || [server.middleware.context.compiler]; + for (let i = 0; i < compilers.length; i++) { + new webpack.ProgressPlugin(function() { + data.progress[i] = arguments; + }).apply(compilers[i]); + } + + let template = options.template; + if (!template) { + if ( + fs + .readdirSync(__dirname) + .filter(x => x.endsWith('.ejs')) + .map(x => x.slice(0, -4)) + .indexOf(options.theme) < 0 + ) + throw new Error(`Unknown theme provided: ${options.theme}`); + template = fs.readFileSync( + path.resolve(__dirname, options.theme + '.ejs'), + 'utf8' + ); + } + + // eslint-disable-next-line no-return-assign + Object.keys(options).forEach(key => (data[key] = options[key])); // expend data with options + + let wasValid = false; + + return async (req, res, next) => { + const valid = data.progress.every(p => p[0] === 1); + wasValid = wasValid || valid; + + if ( + valid || // already valid + (options.disableWhenValid && wasValid) || // if after valid state should be disabled + req.method !== 'GET' + ) { + return await next(); + } else { + res.setHeader('Content-Type', 'text/html'); + res.end(ejs.render(template, data)); + } + }; +}; + +module.exports = webpackDevServerWaitpage; diff --git a/packages/uikit-workshop/build/webpack-dev-server-waitpage/pl-loading.ejs b/packages/uikit-workshop/build/webpack-dev-server-waitpage/pl-loading.ejs new file mode 100644 index 000000000..f41bfd15f --- /dev/null +++ b/packages/uikit-workshop/build/webpack-dev-server-waitpage/pl-loading.ejs @@ -0,0 +1,195 @@ + + + + + + + + + <%= title %> + + + + + + + +
+
Pattern Lab is compiling for the first time.
+ +
+ <% for (var i = 0 ; i < progress.length ; i++) { %> +
+ + + + + <%= Math.round(100 * progress[i][0]) %>% + + + + <% + const capitalize = (str) => { + if (typeof str !== 'string') return ''; + return str.replace(/(?:^|\s)\S/g, function(a) { return a.toUpperCase(); }); + } + + const message = progress[i][1] || ''; + const result = message.includes('building') || message.includes('compiling') + ? 'Compiling' + : message.includes('optimization') + ? 'Optimizing' + : message.includes('emitting') || message.includes('seal') + ? 'Wrapping up' + : progress[i][1]; + %> +
<%= capitalize(result) %>...
+
+ + <% } %> +
+ + +
+ + diff --git a/packages/uikit-workshop/build/webpack-server.js b/packages/uikit-workshop/build/webpack-server.js new file mode 100644 index 000000000..269d583ee --- /dev/null +++ b/packages/uikit-workshop/build/webpack-server.js @@ -0,0 +1,130 @@ +const webpack = require('webpack'); +const express = require('express'); +const browserSync = require('browser-sync').create(); +const webpackDevMiddleware = require('webpack-dev-middleware'); +const opn = require('better-opn'); +const path = require('path'); +const hasha = require('hasha'); +const webpackDevServerWaitpage = require('./webpack-dev-server-waitpage'); +const webpackConfig = require('../webpack.config'); +const app = express(); +const portfinder = require('portfinder'); + +const fileHashes = {}; + +async function serve(patternlab, buildDir = 'public') { + // @todo: move these configs + make customizable? + const root = path.resolve(__dirname, `${buildDir}`); + const preferredPort = 3000; + portfinder.basePort = preferredPort; + + const webpackConfigs = await webpackConfig({ + watch: false, + prod: false, + buildDir: root, + rootDir: process.cwd(), + }); + + const port = await portfinder + .getPortPromise() + .then(port => { + return port; + }) + .catch(err => { + console.log(err); + return 3000; + }); + + // customize bs reload behavior based on the type of asset that's changed + const filesToWatch = [ + `${root}/**/*.css`, + `${root}/**/*.js`, + { + match: [`${root}/**/*.svg`, `${root}/**/*.png`, `${root}/**/*.jpg`], + fn: async function() { + browserSync.reload(); + }, + }, + // only reload the Webpack-generated HTML files when the contents have changed + { + match: [ + path.join(process.cwd(), `${root}/*.html`), + path.join(process.cwd(), `${root}/styleguide/html/*.html`), + ], + fn: async function(event, filePath) { + let updatedHash = false; + + const hash = await hasha.fromFile( + path.resolve(__dirname, `../${filePath}`), + { algorithm: 'md5' } + ); + + if (!fileHashes[filePath] || fileHashes[filePath] !== hash) { + fileHashes[filePath] = hash; + updatedHash = true; + } + + if (updatedHash && !patternlab.isBusy()) { + browserSync.reload(filePath); + } + }, + }, + ]; + + browserSync.init( + { + proxy: `127.0.0.1:${port}`, + logLevel: 'info', + ui: false, + notify: false, + open: false, + tunnel: false, + port, + logFileChanges: false, + reloadOnRestart: true, + watchOptions: { + ignoreInitial: true, + }, + files: filesToWatch, + }, + function(err, bs) { + // assigned port from browsersync based on what's available + const assignedPort = bs.options.get('port'); + opn(`http://localhost:${assignedPort}`); + const compiler = webpack(webpackConfigs); + + app.use( + webpackDevServerWaitpage(compiler, { + proxyHeader: 'browsersync-proxy', + redirectPath: `http://localhost:${assignedPort}`, + }) + ); + + app.use( + webpackDevMiddleware(compiler, { + quiet: true, + stats: 'errors-warnings', + writeToDisk: true, + logLevel: 'error', + }) + ); + + app.use(express.static(root)); + + app.listen(assignedPort, '127.0.0.1', function onStart(error) { + if (error) { + console.log(error); + } + }); + } + ); + + // auto-reload the page when PL finishes compiling + patternlab.events.on('patternlab-build-end', () => { + browserSync.reload(); + }); +} + +module.exports = { + serve, +}; diff --git a/packages/uikit-workshop/package.json b/packages/uikit-workshop/package.json index 8aaa18d00..d76961650 100644 --- a/packages/uikit-workshop/package.json +++ b/packages/uikit-workshop/package.json @@ -4,6 +4,7 @@ "description": "Front-end assets and templates for the default Pattern Lab workshop view", "main": "gulpfile.js", "scripts": { + "start": "node ./build-tools.js", "prepublish": "npm run build", "postbootstrap": "npm run build", "build": "webpack-cli --config webpack.config.js --progress --bail --display=minimal", @@ -31,36 +32,19 @@ "repository": "https://github.com/pattern-lab/patternlab-node/tree/master/packages/uikit-workshop", "bugs": "https://github.com/pattern-lab/patternlab-node/issues", "devDependencies": { - "@svgr/webpack": "^4.1.0", - "autoprefixer": "^9.1.0", - "babel-loader": "^8.0.2", - "clean-css-loader": "^1.0.1", - "clean-webpack-plugin": "0.1.19", - "copy-webpack-plugin": "^5.0.2", - "cosmiconfig": "^5.0.6", - "critical": "^1.3.4", - "critical-css-webpack-plugin": "^0.2.0", - "css-loader": "^1.0.0", - "html-loader": "^0.5.5", - "html-webpack-plugin": "github:jantimon/html-webpack-plugin#webpack-4", - "lit-element": "^2.2.1", - "mini-css-extract-plugin": "^0.4.1", - "no-emit-webpack-plugin": "^1.0.0", - "node-sass": "^4.9.3", - "node-sass-selector-importer": "^5.2.0", - "penthouse": "^1.6.2", - "postcss-loader": "^3.0.0", - "raw-loader": "^0.5.1", - "sass-loader": "^7.1.0", - "style-loader": "^1.0.0", - "svg-sprite-loader": "^4.1.6", - "svg-transform-loader": "^2.0.8", - "svgo": "^1.3.0", - "svgo-loader": "^2.2.1", - "uglifyjs-webpack-plugin": "^1.2.7", - "webpack": "4.26.0", - "webpack-cli": "^3.1.2", - "workbox-build": "^3.4.1" + "@pattern-lab/core": "^5.3.0", + "@pattern-lab/engine-handlebars": "^5.0.0", + "@pattern-lab/engine-mustache": "^5.0.0", + "better-opn": "^1.0.0", + "browser-sync": "^2.26.7", + "ejs": "^2.7.2", + "express": "^4.17.1", + "fs-extra": "^8.1.0", + "hasha": "^5.1.0", + "portfinder": "^1.0.25", + "webpack-dev-middleware": "^3.7.2", + "webpack-hot-middleware": "^2.25.0", + "webpackbar": "^4.0.0" }, "dependencies": { "@babel/core": "^7.6.4", @@ -78,56 +62,82 @@ "@skatejs/element-preact": "^0.0.1", "@skatejs/renderer-lit-html": "^0.2.2", "@skatejs/renderer-preact": "^0.3.3", + "@svgr/webpack": "^4.3.3", "@ungap/url-search-params": "^0.1.2", "@webcomponents/custom-elements": "^1.3.0", - "@webcomponents/shadydom": "^1.6.0", - "@webcomponents/template": "^1.4.0", + "@webcomponents/shadydom": "^1.6.1", + "@webcomponents/template": "^1.4.1", + "autoprefixer": "^9.6.5", + "babel-loader": "^8.0.6", "classnames": "^2.2.6", - "clipboard": "^2.0.1", + "clean-css-loader": "^1.0.1", + "clean-webpack-plugin": "0.1.19", + "clipboard": "^2.0.4", + "copy-webpack-plugin": "^5.0.2", "core-js": "^3.3.6", + "cosmiconfig": "^5.0.6", + "critical-css-webpack-plugin": "^0.2.0", + "critical": "^1.3.4", + "css-loader": "^3.2.0", "deepmerge": "^2.1.1", - "document-register-element": "^1.14.3", + "document-register-element": "^1.13.3", "element-closest": "2.0.2", - "es6-promise": "^4.2.4", + "es6-promise": "^4.2.8", "fg-loadcss": "^2.1.0", "fg-loadjs": "^1.1.0", - "fuse.js": "^3.2.1", - "get-own-property-symbols": "^0.9.2", + "fuse.js": "^3.4.5", + "get-own-property-symbols": "^0.9.4", "hard-source-webpack-plugin-patch": "^0.13.3", "hogan.js": "^3.0.2", "htm": "^1.0.1", + "html-loader": "^0.5.5", + "html-webpack-plugin": "github:jantimon/html-webpack-plugin#webpack-4", "iframe-resizer": "^3.6.5", - "lit-html": "^1.1.1", - "mousetrap": "^1.6.2", - "preact": "8.3.1", + "lit-element": "^2.2.1", + "lit-html": "^1.1.2", + "local-chrome": "^0.1.0", + "mini-css-extract-plugin": "^0.8.0", + "mousetrap": "^1.6.3", + "no-emit-webpack-plugin": "^1.0.0", + "node-sass-selector-importer": "^5.2.0", + "node-sass": "^4.13.0", + "penthouse": "^2.2.2", + "postcss-loader": "^3.0.0", "preact-compat": "3.18.4", "preact-context": "1.1.2", "preact-render-to-string": "^4.1.0", + "preact": "8.3.1", "preload-webpack-plugin": "^3.0.0-beta.3", "prerender-spa-plugin": "^3.4.0", "pretty": "^2.0.0", "prismjs": "^1.15.0", "pwa-helpers": "^0.9.0", - "react": "^16.8.6", - "react-animate-height": "^2.0.4", + "raw-loader": "^3.1.0", "react-autosuggest": "^9.4.2", "react-dom": "^16.8.6", "react-html-parser": "^2.0.2", "react-popper-tooltip": "^2.7.0", - "redux": "3.7.2", "redux-thunk": "^2.3.0", + "redux": "3.7.2", "reselect": "^3.0.1", + "sass-loader": "^8.0.0", "scriptjs": "^2.5.8", "scroll-js": "^2.2.0", "skatejs": "^5.2.4", - "ts-loader": "^6.2.0", + "style-loader": "^1.0.0", + "svg-sprite-loader": "^4.1.6", + "svg-transform-loader": "^2.0.8", + "svgo-loader": "^2.2.1", + "svgo": "^1.3.0", + "ts-loader": "^6.2.1", "url-search-params-polyfill": "^5.0.0", - "webpack": "^4.35.3", - "webpack-cli": "^3.3.5", + "webpack-cli": "^3.3.9", "webpack-merge": "^4.2.2", + "webpack": "^4.41.2", "whendefined": "^0.0.1", "wolfy87-eventemitter": "^5.2.6", - "yargs": "^13.3.0" + "workbox-build": "^3.4.1", + "yargs": "^14.2.0" }, "browserslist": [ "last 2 version", diff --git a/packages/uikit-workshop/src/html/index.html b/packages/uikit-workshop/src/html/index.html index 075adf628..de321a589 100644 --- a/packages/uikit-workshop/src/html/index.html +++ b/packages/uikit-workshop/src/html/index.html @@ -20,29 +20,22 @@ as="image" href="styleguide/images/pattern-lab-logo--on-light.svg" /> - - - - - - -
- - -
-
+ + + +