Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ storybook-static

custom-elements.json
*.css.ts

# Prebundled test dependencies
.test-deps/
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"@web/test-runner-playwright": "^1.0.0",
"autoprefixer": "^10.5.4",
"concurrently": "^10.0.5",
"esbuild": "^0.28.1",
"husky": "^9.1.7",
"igniteui-theming": "^27.5.1",
"lint-staged": "^17.3.0",
Expand Down
81 changes: 81 additions & 0 deletions scripts/prebundle-test-deps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { glob, readFile } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import esbuild from 'esbuild';

const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const BARREL = 'igniteui-webcomponents';

export const TEST_DEPS_DIR = '.test-deps';
export const BARREL_BUNDLE = `/${TEST_DEPS_DIR}/${BARREL}.js`;

/** Named import statements pulling from the barrel, e.g. `import { A, type B } from 'igniteui-webcomponents'` */
const BARREL_IMPORT = new RegExp(`import\\s+(type\\s+)?{([^}]*)}\\s*from\\s*'${BARREL}'`, 'g');

/** lit is left external so the bundle cannot introduce a second copy of the runtime. */
const EXTERNAL = [
'lit',
'lit/*',
'lit-html',
'lit-html/*',
'lit-element',
'lit-element/*',
'@lit/*',
'@lit-labs/*',
];

/**
* Collects the runtime (non-type) symbols `src/` imports from the barrel.
* Aliases resolve to their source name: `θaddThemingController as x` => `θaddThemingController`.
*/
async function collectUsedExports() {
const used = new Set();

for await (const file of glob('src/**/*.ts', { cwd: ROOT })) {
const source = await readFile(path.join(ROOT, file), 'utf8');

for (const [, typeOnly, specifiers] of source.matchAll(BARREL_IMPORT)) {
if (typeOnly) {
continue;
}

for (const specifier of specifiers.split(',')) {
const name = specifier
.trim()
.split(/\s+as\s+/)[0]
.trim();

if (name && !name.startsWith('type ')) {
used.add(name);
}
}
}
}

return Array.from(used).sort();
}

/**
* The dev server serves unbundled ESM, so importing the barrel costs ~250 module
* requests (every component in the library). Tree-shaking it down to the handful
* of symbols `src/` actually uses cuts the test suite from ~41s to ~8s.
*/
export async function prebundleTestDeps() {
const exports = await collectUsedExports();

await esbuild.build({
stdin: {
contents: `export { ${exports.join(', ')} } from '${BARREL}';`,
resolveDir: ROOT,
sourcefile: 'test-deps-facade.js',
loader: 'js',
},
bundle: true,
format: 'esm',
treeShaking: true,
outfile: path.join(ROOT, TEST_DEPS_DIR, `${BARREL}.js`),
conditions: ['browser', 'production'],
external: EXTERNAL,
logLevel: 'error',
});
}
2 changes: 1 addition & 1 deletion test/utils/grid-fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default class GridTestFixture<T extends object> {
}

protected async waitForUpdate() {
await Promise.all([elementUpdated(this.grid), nextFrame]);
await Promise.all([elementUpdated(this.grid), nextFrame()]);
await nextFrame();
}

Expand Down
11 changes: 10 additions & 1 deletion web-test-runner.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { fileURLToPath } from 'node:url';
import { esbuildPlugin } from '@web/dev-server-esbuild';
import { playwrightLauncher } from '@web/test-runner-playwright';
import { BARREL_BUNDLE, prebundleTestDeps, TEST_DEPS_DIR } from './scripts/prebundle-test-deps.js';

const filteredLogs = ['in dev mode'];

Expand All @@ -14,7 +15,7 @@ export default /** @type {import("@web/test-runner").TestRunnerConfig} */ ({
},

coverageConfig: {
exclude: ['node_modules/**/*', '**/styles/**', 'test/**']
exclude: ['node_modules/**/*', `${TEST_DEPS_DIR}/**/*`, '**/styles/**', 'test/**']
},

/** Browsers to run tests on */
Expand All @@ -27,6 +28,14 @@ export default /** @type {import("@web/test-runner").TestRunnerConfig} */ ({
},

plugins: [
{
name: 'prebundled-test-deps',

serverStart: () => prebundleTestDeps(),

// Serve the tree-shaken bundle instead of the unbundled barrel.
resolveImport: ({ source }) => (source === 'igniteui-webcomponents' ? BARREL_BUNDLE : undefined),
},
esbuildPlugin({
ts: true,
tsconfig: fileURLToPath(new URL('./tsconfig.json', import.meta.url)),
Expand Down