From e5ae032c130248d45b23d305221786c6ba009a4d Mon Sep 17 00:00:00 2001 From: Ryan Christian Date: Mon, 7 Feb 2022 01:56:27 -0600 Subject: [PATCH 1/4] chore: Misc cleaning --- packages/cli/lib/lib/output-hooks.js | 41 ------------ .../lib/lib/webpack/webpack-client-config.js | 48 +++++++------- packages/cli/tests/build.test.js | 28 +++++---- packages/cli/tests/client.test.js | 25 -------- packages/cli/tests/lib/cli.js | 22 ++++--- .../subjects/progressive-hydration/index.js | 62 ------------------- .../progressive-hydration/package.json | 4 -- .../progressive-hydration/preact.config.js | 15 ----- .../progressive-hydration/prerender-urls.json | 15 ----- .../progressive-hydration/routes/custom.js | 1 - .../progressive-hydration/routes/home.js | 1 - .../progressive-hydration/routes/route66.js | 1 - 12 files changed, 49 insertions(+), 214 deletions(-) delete mode 100644 packages/cli/lib/lib/output-hooks.js delete mode 100644 packages/cli/tests/subjects/progressive-hydration/index.js delete mode 100644 packages/cli/tests/subjects/progressive-hydration/package.json delete mode 100644 packages/cli/tests/subjects/progressive-hydration/preact.config.js delete mode 100644 packages/cli/tests/subjects/progressive-hydration/prerender-urls.json delete mode 100644 packages/cli/tests/subjects/progressive-hydration/routes/custom.js delete mode 100644 packages/cli/tests/subjects/progressive-hydration/routes/home.js delete mode 100644 packages/cli/tests/subjects/progressive-hydration/routes/route66.js diff --git a/packages/cli/lib/lib/output-hooks.js b/packages/cli/lib/lib/output-hooks.js deleted file mode 100644 index 781891f04..000000000 --- a/packages/cli/lib/lib/output-hooks.js +++ /dev/null @@ -1,41 +0,0 @@ -const HOOKS = [ - { - test: /^total precache size /i, - handler: text => `\n \u001b[32m${text}\u001b[39m`, - }, - { - test: /DeprecationWarning/, - handler: () => false, - }, -]; - -function wrap(stream, handler) { - let write = stream.write; - stream.write = text => { - const transformed = handler(text); - if (transformed === false) return; - return write.call(stream, transformed); - }; - return () => { - stream.write = write; - }; -} - -function invokeHooks(text) { - for (let i = HOOKS.length; i--; ) { - let hook = HOOKS[i]; - if (hook.test.test(text)) { - text = hook.handler(text); - } - } - return text; -} - -module.exports = function () { - let out = wrap(process.stdout, invokeHooks), - err = wrap(process.stderr, invokeHooks); - return () => { - out(); - err(); - }; -}; diff --git a/packages/cli/lib/lib/webpack/webpack-client-config.js b/packages/cli/lib/lib/webpack/webpack-client-config.js index 84c4289fb..6e2c45e7c 100644 --- a/packages/cli/lib/lib/webpack/webpack-client-config.js +++ b/packages/cli/lib/lib/webpack/webpack-client-config.js @@ -29,7 +29,7 @@ const cleanFilename = name => * @returns {Promise} */ async function clientConfig(env) { - const { isProd, source, src, cwd /*, port? */ } = env; + const { source, src, cwd } = env; const IS_SOURCE_PREACT_X_OR_ABOVE = isInstalledVersionPreactXOrAbove(cwd); const asyncLoader = IS_SOURCE_PREACT_X_OR_ABOVE ? require.resolve('@preact/async-loader') @@ -84,7 +84,7 @@ async function clientConfig(env) { // copy any static files existsSync(source('assets')) && { from: 'assets', to: 'assets' }, // copy sw-debug - !isProd && { + !env.isProd && { from: resolve(__dirname, '../../resources/sw-debug.js'), to: 'sw-debug.js', }, @@ -100,7 +100,7 @@ async function clientConfig(env) { output: { path: env.dest, publicPath: '/', - filename: isProd ? '[name].[chunkhash:5].js' : '[name].js', + filename: env.isProd ? '[name].[chunkhash:5].js' : '[name].js', chunkFilename: '[name].chunk.[chunkhash:5].js', }, @@ -143,6 +143,8 @@ async function clientConfig(env) { plugins: [ new webpack.DefinePlugin({ 'process.env.ES_BUILD': false, + 'process.env.ADD_SW': env.sw, + 'process.env.PRERENDER': env.prerender, }), new PushManifestPlugin(env), ...(await renderHTMLPlugin(env)), @@ -156,14 +158,12 @@ async function clientConfig(env) { }; } -function getBabelEsmPlugin(config) { +function getBabelEsmPlugin(env) { const esmPlugins = []; - if (config.esm) { + if (env.esm) { esmPlugins.push( new BabelEsmPlugin({ - filename: config.isProd - ? '[name].[chunkhash:5].esm.js' - : '[name].esm.js', + filename: env.isProd ? '[name].[chunkhash:5].esm.js' : '[name].esm.js', chunkFilename: '[name].chunk.[chunkhash:5].esm.js', excludedPlugins: ['BabelEsmPlugin', 'InjectManifest'], beforeStartExecution: plugins => { @@ -196,7 +196,7 @@ function getBabelEsmPlugin(config) { /** * @returns {import('webpack').Configuration} */ -function isProd(config) { +function isProd(env) { let limit = 200 * 1000; // 200kb const prodConfig = { performance: Object.assign( @@ -205,14 +205,12 @@ function isProd(config) { maxAssetSize: limit, maxEntrypointSize: limit, }, - config.pkg.performance + env.pkg.performance ), plugins: [ new webpack.DefinePlugin({ - 'process.env.ADD_SW': config.sw, - 'process.env.ESM': config.esm, - 'process.env.PRERENDER': config.prerender, + 'process.env.ESM': env.esm, }), new SizePlugin(), ], @@ -252,7 +250,7 @@ function isProd(config) { }, }; - if (config['inline-css']) { + if (env['inline-css']) { prodConfig.plugins.push( new CrittersPlugin({ preload: 'media', @@ -263,11 +261,11 @@ function isProd(config) { ); } - if (config.analyze) { + if (env.analyze) { prodConfig.plugins.push(new BundleAnalyzerPlugin()); } - if (config.brotli) { + if (env.brotli) { prodConfig.plugins.push( new CompressionPlugin({ filename: '[path].br[query]', @@ -283,8 +281,8 @@ function isProd(config) { /** * @returns {import('webpack').Configuration} */ -function isDev(config) { - const { cwd, src, refresh } = config; +function isDev(env) { + const { cwd, src } = env; return { infrastructureLogging: { @@ -292,12 +290,8 @@ function isDev(config) { }, plugins: [ new webpack.NamedModulesPlugin(), - ...(refresh ? [new RefreshPlugin()] : []), - new webpack.DefinePlugin({ - 'process.env.ADD_SW': config.sw, - 'process.env.PRERENDER': config.prerender, - }), - ], + env.refresh && new RefreshPlugin(), + ].filter(Boolean), devServer: { hot: true, @@ -312,9 +306,9 @@ function isDev(config) { ignored: [resolve(cwd, 'build'), resolve(cwd, 'node_modules')], }, }, - https: config.https, - port: config.port, - host: process.env.HOST || config.host || '0.0.0.0', + https: env.https, + port: env.port, + host: process.env.HOST || env.host || '0.0.0.0', allowedHosts: 'all', historyApiFallback: true, client: { diff --git a/packages/cli/tests/build.test.js b/packages/cli/tests/build.test.js index 818782f1c..16e334829 100644 --- a/packages/cli/tests/build.test.js +++ b/packages/cli/tests/build.test.js @@ -1,13 +1,10 @@ const { join } = require('path'); -const { existsSync } = require('fs'); -const { readFile } = require('fs').promises; +const { access, readdir, readFile } = require('fs').promises; const looksLike = require('html-looks-like'); const { create, build } = require('./lib/cli'); const { snapshot } = require('./lib/utils'); const { subject } = require('./lib/output'); const images = require('./images/build'); -const { promisify } = require('util'); -const glob = promisify(require('glob').glob); const minimatch = require('minimatch'); const prerenderUrlFiles = [ @@ -79,12 +76,17 @@ describe('preact build', () => { it('should use custom `.babelrc`', async () => { // app with custom .babelrc enabling async functions - let app = await subject('custom-babelrc'); + let dir = await subject('custom-babelrc'); - await build(app); + await build(dir); - const bundleFiles = await glob(`${app}/build/bundle.*.js`); - const transpiledChunk = await readFile(bundleFiles[0], 'utf8'); + const bundleFile = (await readdir(`${dir}/build`)).find(file => + /bundle\.\w{5}\.js$/.test(file) + ); + const transpiledChunk = await readFile( + `${dir}/build/${bundleFile}`, + 'utf8' + ); // when tragetting only last 1 chrome version, babel preserves // arrow function. So checking for the delay function code from delay function in @@ -183,8 +185,8 @@ describe('preact build', () => { let dir = await subject('custom-webpack'); await build(dir); - let file = join(dir, 'build/bundle.js'); - expect(existsSync(file)).toBe(true); + let stableOutput = join(dir, 'build/bundle.js'); + expect(await access(stableOutput)).toBeUndefined(); }); it('should use custom `template.html`', async () => { @@ -218,13 +220,13 @@ describe('preact build', () => { let dir = await subject('static-root'); await build(dir); let file = join(dir, 'build', '.htaccess'); - expect(existsSync(file)).toBe(true); + expect(await access(file)).toBeUndefined(); }); - it('should error out for invalid argument', async () => { + it('should error out for invalid CLI argument', async () => { let dir = await subject('custom-template'); const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {}); - expect(build(dir, { 'service-worker': false })).rejects.toEqual( + await expect(build(dir, { 'service-worker': false })).rejects.toEqual( new Error('Invalid argument found.') ); expect(mockExit).toHaveBeenCalledWith(1); diff --git a/packages/cli/tests/client.test.js b/packages/cli/tests/client.test.js index 67c2b47a5..20dc2f265 100644 --- a/packages/cli/tests/client.test.js +++ b/packages/cli/tests/client.test.js @@ -22,31 +22,6 @@ describe('client-side tests', () => { PORT = await getPort(); }); - it.skip('should hydrate routes progressively.', async () => { - let dir = await subject('progressive-hydration'); - await build(dir); - const server = getServer(join(dir, 'build'), PORT); - - // let page = await loadPage(chrome, `http://127.0.0.1:${PORT}/`); - const page = await chrome.newPage(); - - page.on('console', consoleMessage => { - // eslint-disable-next-line - console[consoleMessage.type()](consoleMessage.text()); - }); - - await page.goto(`http://127.0.0.1:${PORT}/`); - - // await waitUntilExpression(page, `window.booted`); - await sleep(500); - - const mutations = await page.evaluate('window.mutations'); - - expect(mutations).toHaveLength(0); - - server.server.close(); - }); - it('should hydrate routes progressively with preact8.', async () => { let dir = await subject('progressive-hydration-preact8'); await build(dir, {}, true); diff --git a/packages/cli/tests/lib/cli.js b/packages/cli/tests/lib/cli.js index fb2912801..6e5b2c8a0 100644 --- a/packages/cli/tests/lib/cli.js +++ b/packages/cli/tests/lib/cli.js @@ -1,13 +1,16 @@ const { join } = require('path'); -const { mkdirSync, symlinkSync } = require('fs'); +const { mkdir, symlink } = require('fs').promises; const cmd = require('../../lib/commands'); const { tmpDir } = require('./output'); const shell = require('shelljs'); const root = join(__dirname, '../../../..'); -function linkPackage(name, from, to) { - symlinkSync(join(from, 'node_modules', name), join(to, 'node_modules', name)); +async function linkPackage(name, from, to) { + await symlink( + join(from, 'node_modules', name), + join(to, 'node_modules', name) + ); } const argv = { @@ -28,17 +31,18 @@ exports.create = async function (template, name) { return dest; }; -exports.build = function (cwd, options, installNodeModules = false) { +exports.build = async function (cwd, options, installNodeModules = false) { if (!installNodeModules) { - mkdirSync(join(cwd, 'node_modules'), { recursive: true }); // ensure exists, avoid exit() - linkPackage('preact', root, cwd); - linkPackage('preact-render-to-string', root, cwd); + await mkdir(join(cwd, 'node_modules'), { recursive: true }); // ensure exists, avoid exit() + await linkPackage('preact', root, cwd); + await linkPackage('preact-render-to-string', root, cwd); } else { shell.cd(cwd); shell.exec('npm i'); } - let opts = Object.assign({ cwd }, argv); - return cmd.build(argv.src, Object.assign({}, opts, options)); + + let opts = Object.assign({}, { cwd }, argv, options); + return await cmd.build(argv.src, opts); }; exports.watch = function (cwd, port, host = '127.0.0.1') { diff --git a/packages/cli/tests/subjects/progressive-hydration/index.js b/packages/cli/tests/subjects/progressive-hydration/index.js deleted file mode 100644 index bddd9e0c8..000000000 --- a/packages/cli/tests/subjects/progressive-hydration/index.js +++ /dev/null @@ -1,62 +0,0 @@ -import { Router } from 'preact-router'; -import Home from './routes/home'; -import Route66 from './routes/route66'; -import Custom from './routes/custom'; - -if (typeof window !== 'undefined') { - window.mutations = []; - window.nativeMutations = []; - - const serialize = val => { - if (val instanceof NodeList) return [].map.call(val, serialize); - if (val instanceof Node) { - if (val.splitText) return val; - return val.outerHTML.match(/.*?>/)[0]; - } - if (Array.isArray(val)) return val.map(serialize); - if (val != null && typeof val == 'object') { - let node = {}; - for (let i in val) node[i] = serialize(val[i]); - return node; - } - return val; - }; - - const observer = new MutationObserver(records => { - for (let mutation of records) { - const m = serialize(mutation); - for (let i in m) - if (m[i] == null || (Array.isArray(m[i]) && !m[i].length)) delete m[i]; - window.mutations.push(m); - window.nativeMutations.push(mutation); - } - }); - observer.observe(document.body, { - attributes: true, - childList: true, - subtree: true, - }); - - setTimeout(() => { - // eslint-disable-next-line - console.log('BOOTED'); - window.booted = true; - }, 10); -} - -export default function App({ url, ...props }) { - return ( -
- - - - - - -
- ); -} diff --git a/packages/cli/tests/subjects/progressive-hydration/package.json b/packages/cli/tests/subjects/progressive-hydration/package.json deleted file mode 100644 index 8f8310dad..000000000 --- a/packages/cli/tests/subjects/progressive-hydration/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "private": true, - "name": "preact-prerender" -} diff --git a/packages/cli/tests/subjects/progressive-hydration/preact.config.js b/packages/cli/tests/subjects/progressive-hydration/preact.config.js deleted file mode 100644 index 2c70587c4..000000000 --- a/packages/cli/tests/subjects/progressive-hydration/preact.config.js +++ /dev/null @@ -1,15 +0,0 @@ -const path = require('path'); - -module.exports = function (config) { - const loader = path.resolve(__dirname, '../../../../async-loader/async.js'); - const alias = config.resolve.alias; - alias['preact-cli/async-component'] = alias[ - '@preact/async-loader/async' - ] = loader; - - for (const rule of config.module.rules) { - if (/async-loader/.test(rule.loader)) { - rule.loader = path.resolve(__dirname, '../../../../async-loader'); - } - } -}; diff --git a/packages/cli/tests/subjects/progressive-hydration/prerender-urls.json b/packages/cli/tests/subjects/progressive-hydration/prerender-urls.json deleted file mode 100644 index 7d52bcdaf..000000000 --- a/packages/cli/tests/subjects/progressive-hydration/prerender-urls.json +++ /dev/null @@ -1,15 +0,0 @@ -[ - { - "url": "/", - "title": "Home" - }, - { - "url": "/route66", - "title": "Route66" - }, - { - "url": "/custom", - "title": "Custom", - "myProp": "It worked!" - } -] diff --git a/packages/cli/tests/subjects/progressive-hydration/routes/custom.js b/packages/cli/tests/subjects/progressive-hydration/routes/custom.js deleted file mode 100644 index 6d44fbc91..000000000 --- a/packages/cli/tests/subjects/progressive-hydration/routes/custom.js +++ /dev/null @@ -1 +0,0 @@ -export default ({ myProp }) =>
{myProp}
; diff --git a/packages/cli/tests/subjects/progressive-hydration/routes/home.js b/packages/cli/tests/subjects/progressive-hydration/routes/home.js deleted file mode 100644 index ee170cf6d..000000000 --- a/packages/cli/tests/subjects/progressive-hydration/routes/home.js +++ /dev/null @@ -1 +0,0 @@ -export default () =>
Home
; diff --git a/packages/cli/tests/subjects/progressive-hydration/routes/route66.js b/packages/cli/tests/subjects/progressive-hydration/routes/route66.js deleted file mode 100644 index cf08af328..000000000 --- a/packages/cli/tests/subjects/progressive-hydration/routes/route66.js +++ /dev/null @@ -1 +0,0 @@ -export default () =>
Route66
; From b9cfb784c19e478a0f8676af94e8b3ae38ef8293 Mon Sep 17 00:00:00 2001 From: Ryan Christian Date: Tue, 8 Feb 2022 15:58:20 -0600 Subject: [PATCH 2/4] fix: Correcting push-manifest in non-ESM builds --- .../lib/lib/webpack/create-load-manifest.js | 6 +-- packages/cli/tests/build.test.js | 10 +++++ packages/cli/tests/images/build.js | 43 ++++++++++++++++++- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/packages/cli/lib/lib/webpack/create-load-manifest.js b/packages/cli/lib/lib/webpack/create-load-manifest.js index de1ecf746..da18f2609 100644 --- a/packages/cli/lib/lib/webpack/create-load-manifest.js +++ b/packages/cli/lib/lib/webpack/create-load-manifest.js @@ -8,7 +8,7 @@ module.exports = (assets, isESMBuild = false, namedChunkGroups) => { styles = []; for (let filename in assets) { if (!/\.map$/.test(filename)) { - if (/route-/.test(filename)) { + if (/^route-.*\.js$/.test(filename)) { // both ESM & regular match here isMatch(filename, isESMBuild) && scripts.push(filename); } else if (/chunk\.(.+)\.css$/.test(filename)) { @@ -39,8 +39,8 @@ module.exports = (assets, isESMBuild = false, namedChunkGroups) => { }; let path, css, obj; - scripts.forEach((filename, idx) => { - css = styles[idx]; + scripts.forEach(filename => { + css = styles.find(asset => asset.startsWith(filename.replace(/\..*/, ''))); obj = Object.assign({}, defaults); obj[filename] = { type: 'script', weight: 0.9 }; if (css) obj[css] = { type: 'style', weight: 0.9 }; diff --git a/packages/cli/tests/build.test.js b/packages/cli/tests/build.test.js index 16e334829..f17bcdc21 100644 --- a/packages/cli/tests/build.test.js +++ b/packages/cli/tests/build.test.js @@ -232,4 +232,14 @@ describe('preact build', () => { expect(mockExit).toHaveBeenCalledWith(1); mockExit.mockRestore(); }); + + it('should produce correct push-manifest', async () => { + let dir = await create('default'); + + await build(dir); + const manifest = await readFile(`${dir}/build/push-manifest.json`, 'utf8'); + expect(manifest).toEqual( + expect.stringMatching(getRegExpFromMarkup(images.pushManifest)) + ); + }); }); diff --git a/packages/cli/tests/images/build.js b/packages/cli/tests/images/build.js index a29cb8324..03a2b5a55 100644 --- a/packages/cli/tests/images/build.js +++ b/packages/cli/tests/images/build.js @@ -22,7 +22,7 @@ exports.default = Object.assign({}, common, { 'index.html': 2034, 'manifest.json': 455, 'preact_prerender_data.json': 11, - 'push-manifest.json': 812, + 'push-manifest.json': 450, 'route-home.chunk.bcb8a.css': 58, 'route-home.chunk.3cec8.js': 327, 'route-home.chunk.3cec8.js.map': 483, @@ -212,3 +212,44 @@ exports.template = ` `; + +exports.pushManifest = ` +{ + "/":{ + "bundle.\\w{5}.css":{ + "type":"style", + "weight":1 + }, + "bundle.\\w{5}.js":{ + "type":"script", + "weight":1 + }, + "route-home.chunk.\\w{5}.js":{ + "type":"script", + "weight":0.9 + }, + "route-home.chunk.\\w{5}.css":{ + "type":"style", + "weight":0.9 + } + }, + "/profile":{ + "bundle.\\w{5}.css":{ + "type":"style", + "weight":1 + }, + "bundle.\\w{5}.js":{ + "type":"script", + "weight":1 + }, + "route-profile.chunk.\\w{5}.js":{ + "type":"script", + "weight":0.9 + }, + "route-profile.chunk.\\w{5}.css":{ + "type":"style", + "weight":0.9 + } + } +} +`; From ac53c63a1c3611f654af16cb01dbfdd2b145e79a Mon Sep 17 00:00:00 2001 From: Ryan Christian Date: Tue, 8 Feb 2022 15:58:38 -0600 Subject: [PATCH 3/4] test: Adding TS template to build suite --- packages/cli/tests/build.test.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/cli/tests/build.test.js b/packages/cli/tests/build.test.js index f17bcdc21..76934f1ca 100644 --- a/packages/cli/tests/build.test.js +++ b/packages/cli/tests/build.test.js @@ -6,6 +6,7 @@ const { snapshot } = require('./lib/utils'); const { subject } = require('./lib/output'); const images = require('./images/build'); const minimatch = require('minimatch'); +const shell = require('shelljs'); const prerenderUrlFiles = [ 'prerender-urls.json', @@ -66,6 +67,17 @@ describe('preact build', () => { testMatch(output, images['default-esm']); }); + it(`builds the 'typescript' template`, async () => { + let dir = await create('typescript'); + + // The tsconfig.json in the template covers the test directory, + // so TS will error out if it can't find even test-only module definitions + shell.cd(dir); + shell.exec('npm i @types/enzyme enzyme-adapter-preact-pure'); + + expect(() => build(dir)).not.toThrow(); + }); + it('should use SASS styles', async () => { let dir = await subject('sass'); await build(dir); From 00a3cd3abb64778bddf20c150a369019ef376bd5 Mon Sep 17 00:00:00 2001 From: Ryan Christian Date: Tue, 8 Feb 2022 16:06:42 -0600 Subject: [PATCH 4/4] docs: Adding changeset --- .changeset/few-pears-push.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/few-pears-push.md diff --git a/.changeset/few-pears-push.md b/.changeset/few-pears-push.md new file mode 100644 index 000000000..51cbb9ba2 --- /dev/null +++ b/.changeset/few-pears-push.md @@ -0,0 +1,5 @@ +--- +'preact-cli': patch +--- + +Corrects `push-manifest.json` generation in non-ESM builds