Skip to content

Commit

Permalink
perf: speed up docs builds (#1297)
Browse files Browse the repository at this point in the history
Cache 11ty import map between builds
  • Loading branch information
bennypowers authored Oct 24, 2023
1 parent 54264f3 commit 0f58ce0
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 55 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
node_modules
.wireit
.rollup.cache
.cache

# Ignore compiled files in webroot
_site
Expand Down
145 changes: 92 additions & 53 deletions docs/_plugins/importMap.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand All @@ -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);
Expand Down Expand Up @@ -87,39 +101,64 @@ 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
// eslint-disable-next-line no-console
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();
}
});
};
8 changes: 6 additions & 2 deletions eleventy.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 0f58ce0

Please sign in to comment.