diff --git a/.gitignore b/.gitignore index 066be7c8d1..12b4b78b54 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ node_modules .wireit .rollup.cache +.cache # Ignore compiled files in webroot _site diff --git a/docs/_plugins/importMap.cjs b/docs/_plugins/importMap.cjs index 16a614e63f..55e83a98a6 100644 --- a/docs/_plugins/importMap.cjs +++ b/docs/_plugins/importMap.cjs @@ -2,33 +2,47 @@ const { join } = require('node:path'); const { pathToFileURL } = require('node:url'); const { glob } = require('glob'); +const { AssetCache } = require('@11ty/eleventy-fetch'); -module.exports = function(eleventyConfig, { - inputMap = undefined, - defaultProvider = undefined, - localPackages = [], -} = {}) { - const cwd = process.cwd(); - const elementsDir = join(cwd, 'elements/'); - - const specs = localPackages.map(spec => ({ - spec, - packageName: spec.replace(/^@/, '$').replace(/@.*$/, '').replace(/^\$/, '@') - })); - - // copy over local packages - for (const { packageName } of specs) { - eleventyConfig.addPassthroughCopy({ [`node_modules/${packageName}`]: `/assets/packages/${packageName}` }); - } - - // HACK: copy lit transitive deps - // this might not be necessary if we flatten to a single lit version - for (const packageName of ['lit-html', 'lit-element']) { - eleventyConfig.addPassthroughCopy({ [`node_modules/${packageName}`]: `/assets/packages/${packageName}` }); +function logPerf() { + // We should log performance regressions + /* eslint-disable no-console */ + const chalk = require('chalk'); + const TOTAL = performance.measure('importMap-total', 'importMap-start', 'importMap-end'); + const RESOLVE = performance.measure('importMap-resolve', 'importMap-start', 'importMap-afterLocalPackages'); + const TRACE = performance.measure('importMap-trace', 'importMap-afterLocalPackages', 'importMap-afterRHDSTraces'); + if (TOTAL.duration > 2000) { + console.log( + `🦥 Import map generator done in ${chalk.red(TOTAL.duration)}ms\n`, + ` Resolving local packages took ${chalk.red(RESOLVE.duration)}ms\n`, + ` Tracing RHDS sources took ${chalk.red(TRACE.duration)}ms`, + ); + } else if (TOTAL.duration > 1000) { + console.log( + `🐢 Import map generator done in ${chalk.yellow(TOTAL.duration)}ms\n`, + ` Resolving local packages took ${chalk.yellow(RESOLVE.duration)}ms\n`, + ` Tracing RHDS sources took ${chalk.yellow(TRACE.duration)}ms`, + ); + } else { + console.log( + `⚡ Import map generator done in ${chalk.blue(TOTAL.duration)}ms\n`, + ); } - // ENDHACK + /* eslint-enable no-console */ +} - eleventyConfig.addGlobalData('importMap', async function importMap() { +async function getCachedImportMap({ + defaultProvider, + inputMap, + specs, + localPackages, + elementsDir, + cwd, + assetCache, +}) { + if (assetCache.isCacheValid('1d')) { + return assetCache.getCachedValue(); + } else { try { performance.mark('importMap-start'); @@ -51,8 +65,8 @@ module.exports = function(eleventyConfig, { const traces = []; for (const x of await glob('./*/*.ts', { cwd: elementsDir, dotRelative: true, ignore: '**/*.d.ts' })) { traces.push( - generator.traceInstall(x.replace('./', elementsDir).replace('.ts', '.js')), - generator.traceInstall(x.replace('./', '@rhds/elements/').replace('.ts', '.js')), + generator.link(x.replace('./', elementsDir).replace('.ts', '.js')), + generator.link(x.replace('./', '@rhds/elements/').replace('.ts', '.js')), ); } await Promise.all(traces); @@ -87,6 +101,8 @@ module.exports = function(eleventyConfig, { logPerf(); + assetCache.save(json, 'json'); + return json; } catch (e) { // it's important to surface this, even if it means double-logging @@ -94,32 +110,55 @@ module.exports = function(eleventyConfig, { console.error(e); throw e; } - }); -}; - -function logPerf() { - // We should log performance regressions - /* eslint-disable no-console */ - const chalk = require('chalk'); - const TOTAL = performance.measure('importMap-total', 'importMap-start', 'importMap-end'); - const RESOLVE = performance.measure('importMap-resolve', 'importMap-start', 'importMap-afterLocalPackages'); - const TRACE = performance.measure('importMap-trace', 'importMap-afterLocalPackages', 'importMap-afterRHDSTraces'); - if (TOTAL.duration > 2000) { - console.log( - `🦥 Import map generator done in ${chalk.red(TOTAL.duration)}ms\n`, - ` Resolving local packages took ${chalk.red(RESOLVE.duration)}ms\n`, - ` Tracing RHDS sources took ${chalk.red(TRACE.duration)}ms`, - ); - } else if (TOTAL.duration > 1000) { - console.log( - `🐢 Import map generator done in ${chalk.yellow(TOTAL.duration)}ms\n`, - ` Resolving local packages took ${chalk.yellow(RESOLVE.duration)}ms\n`, - ` Tracing RHDS sources took ${chalk.yellow(TRACE.duration)}ms`, - ); - } else { - console.log( - `⚡ Import map generator done in ${chalk.blue(TOTAL.duration)}ms\n`, - ); } - /* eslint-enable no-console */ } + +module.exports = function(eleventyConfig, { + inputMap = undefined, + defaultProvider = undefined, + localPackages = [], +} = {}) { + const cwd = process.cwd(); + const elementsDir = join(cwd, 'elements/'); + + const specs = localPackages.map(spec => ({ + spec, + packageName: spec.replace(/^@/, '$').replace(/@.*$/, '').replace(/^\$/, '@') + })); + + // copy over local packages + for (const { packageName } of specs) { + eleventyConfig.addPassthroughCopy({ [`node_modules/${packageName}`]: `/assets/packages/${packageName}` }); + } + + // HACK: copy lit transitive deps + // this might not be necessary if we flatten to a single lit version + for (const packageName of ['lit-html', 'lit-element']) { + eleventyConfig.addPassthroughCopy({ [`node_modules/${packageName}`]: `/assets/packages/${packageName}` }); + } + // ENDHACK + + const assetCache = new AssetCache('rhds-ux-dot-import-map'); + + eleventyConfig.addGlobalData('importMap', async function cacheImportMap() { + return getCachedImportMap({ + defaultProvider, + inputMap, + specs, + localPackages, + elementsDir, + cwd, + assetCache, + }); + }); + + eleventyConfig.on('eleventy.beforeWatch', async function(/** @type {string[]} */ changedFiles) { + const files = + changedFiles.filter(x => x.match(/eleventy\.config\.c?js$|importMap\.c?js$/)); + if (files.length) { + // eslint-disable-next-line no-console + console.log(`${files.join(', ')} changed, invalidating importmap cache`); + assetCache.cache.destroy(); + } + }); +}; diff --git a/eleventy.config.cjs b/eleventy.config.cjs index 726d77e9d6..acc1cf5ac2 100644 --- a/eleventy.config.cjs +++ b/eleventy.config.cjs @@ -60,20 +60,24 @@ module.exports = function(eleventyConfig) { eleventyConfig.addPlugin(ImportMapPlugin, { defaultProvider: 'nodemodules', localPackages: [ + // ux-dot dependencies 'fuse.js', 'element-internals-polyfill', + + // RHDS dependencies 'lit', '@lit/reactive-element', 'tslib', '@floating-ui/dom', '@floating-ui/core', - // + + // RHDS modules '@rhds/tokens', '@rhds/tokens/media.js', '@rhds/tokens/meta.js', '@patternfly/pfe-core', '@patternfly/elements', - '@rhds/tokens', + // extra modules used in demo that didn't get picked up in the sources trace // future solution could be to inject maps into each page in a transform // but that could be prohibitively expensive if it has to call out to network for each page