blob: b2a7981869e0048d90fe5c67db582afed191db56 [file]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {assert} from 'chai';
import fs from 'node:fs';
import path from 'node:path';
import sinon from 'sinon';
import {devtoolsPlugin, esbuildPlugin} from '../devtools_plugin.js';
describe('devtools_plugin can compute paths with', () => {
const devtoolsRoot = path.resolve(import.meta.dirname, '../../..');
const genRoot = path.join(devtoolsRoot, 'out/Default/gen');
const externalFiles = new Set([
'front_end/core/common/common.js',
'front_end/core/sdk/sdk.js',
'front_end/third_party/lighthouse/lighthouse-dt-bundle.js',
]);
it('throws when externalFiles is not provided', () => {
// @ts-expect-error
assert.throws(() => devtoolsPlugin('./AnotherFile.js', 'front_end/core/sdk/FirstFile.js'),
/requires an externalFiles Set/);
});
it('throws when root or genRoot is not provided', () => {
// @ts-expect-error
assert.throws(() => devtoolsPlugin('./AnotherFile.js', 'front_end/core/sdk/FirstFile.js', externalFiles),
/requires a root path/);
});
it('same directory import not in externalFiles', () => {
const importer = path.join(devtoolsRoot, 'front_end/core/sdk/FirstFile.js');
assert.deepEqual(
devtoolsPlugin('./AnotherFile.js', importer, externalFiles, devtoolsRoot, genRoot),
{
id: path.join(devtoolsRoot, 'front_end/core/sdk/AnotherFile.js'),
external: false,
},
);
});
it('different directory import matching externalFiles', () => {
const importer = path.join(devtoolsRoot, 'front_end/core/sdk/FirstFile.js');
assert.deepEqual(
devtoolsPlugin('../common/common.js', importer, externalFiles, devtoolsRoot, genRoot),
{
id: path.join(devtoolsRoot, 'front_end/core/common/common.js'),
external: true,
},
);
});
it('node built-in modules', () => {
assert.deepEqual(devtoolsPlugin('fs', 'scripts/some-script.js', externalFiles, devtoolsRoot, genRoot), {
id: 'fs',
external: true,
});
});
it('importing generated files', () => {
const importer = path.join(devtoolsRoot, 'front_end/core/sdk/FirstFile.js');
assert.deepEqual(
devtoolsPlugin('../../generated/protocol.js', importer, externalFiles, devtoolsRoot, genRoot),
{
id: path.join(devtoolsRoot, 'front_end/generated/protocol.js'),
external: false,
},
);
});
it('importing lighthouse files in externalFiles', () => {
const importer = path.join(devtoolsRoot, 'front_end/core/sdk/FirstFile.js');
assert.deepEqual(
devtoolsPlugin(
'../../third_party/lighthouse/lighthouse-dt-bundle.js',
importer,
externalFiles,
devtoolsRoot,
genRoot,
),
{
id: path.join(devtoolsRoot, 'front_end/third_party/lighthouse/lighthouse-dt-bundle.js'),
external: true,
},
);
});
});
describe('esbuild_plugin can compute paths with', () => {
const devtoolsRoot = path.resolve(import.meta.dirname, '../../..');
const genRoot = path.join(devtoolsRoot, 'out/Default/gen');
const outdir = path.join(genRoot, 'front_end/core/sdk');
const externalFiles = new Set([
'front_end/core/common/common.js',
'front_end/core/sdk/sdk.js',
'front_end/panels/timeline/timeline-meta.js',
'front_end/third_party/lighthouse/lighthouse-dt-bundle.js',
'front_end/third_party/puppeteer/puppeteer.js',
]);
const plugin = esbuildPlugin(outdir, genRoot, devtoolsRoot, externalFiles);
it('throws when externalFiles is not provided', () => {
// @ts-expect-error
assert.throws(() => esbuildPlugin(outdir, genRoot, devtoolsRoot), /requires an externalFiles Set/);
});
it('throws when outdir, genRoot, or rootDir is not provided', () => {
// @ts-expect-error
assert.throws(() => esbuildPlugin(undefined, genRoot, devtoolsRoot, externalFiles), /requires an outdir path/);
});
it('same directory import not in externalFiles', () => {
assert.deepEqual(
plugin({
path: './AnotherFile.js',
importer: path.join(devtoolsRoot, 'front_end/core/sdk/FirstFile.js'),
}),
{path: path.join(devtoolsRoot, 'front_end', 'core', 'sdk', 'AnotherFile.js')},
);
});
it('different directory import in externalFiles', () => {
assert.deepEqual(
plugin({
path: '../common/common.js',
importer: path.join(devtoolsRoot, 'front_end/core/sdk/FirstFile.js'),
}),
{
path: '../common/common.js',
external: true,
},
);
});
it('node built-in modules', () => {
assert.deepEqual(
plugin({
path: 'fs',
importer: path.join(devtoolsRoot, 'scripts/some-script.js'),
}),
{path: 'fs', external: true},
);
});
it('codemirror modules', () => {
assert.deepEqual(
plugin({
path: '../../lib/codemirror',
importer: path.join(devtoolsRoot, 'scripts/some-script.js'),
}),
{path: '../../lib/codemirror', external: true},
);
});
it('importing lighthouse files in externalFiles', () => {
assert.deepEqual(
plugin({
path: '../../third_party/lighthouse/lighthouse-dt-bundle.js',
importer: path.join(devtoolsRoot, 'front_end/core/sdk/FirstFile.js'),
}),
{
path: '../../third_party/lighthouse/lighthouse-dt-bundle.js',
external: true,
},
);
});
it('importing generated files', () => {
assert.deepEqual(
plugin({
path: '../../generated/protocol.js',
importer: path.join(devtoolsRoot, 'front_end/core/sdk/FirstFile.js'),
}),
{
path: path.join(devtoolsRoot, 'front_end/generated/protocol.ts'),
},
);
});
it('resolves TypeScript source files when importing JS', () => {
const importer = path.join(devtoolsRoot, 'front_end/core/root/root.ts');
const jsFile = path.join(devtoolsRoot, 'front_end/core/root/FakeModule.js');
const tsFile = path.join(devtoolsRoot, 'front_end/core/root/FakeModule.ts');
const stub = sinon.stub(fs, 'existsSync');
stub.callThrough();
stub.withArgs(jsFile).returns(false);
stub.withArgs(tsFile).returns(true);
try {
const res = plugin({
path: './FakeModule.js',
importer,
});
assert.deepEqual(res, {
path: tsFile,
});
} finally {
stub.restore();
}
});
it('resolves generated CSS files from gen directory', () => {
const legacyOutdir = path.join(genRoot, 'front_end/ui/legacy');
const legacyPlugin = esbuildPlugin(legacyOutdir, genRoot, devtoolsRoot, externalFiles);
const importer = path.join(devtoolsRoot, 'front_end/ui/legacy/legacy.ts');
const jsFile = path.join(devtoolsRoot, 'front_end/ui/legacy/fakeCss.css.js');
const cssGenFile = path.join(genRoot, 'front_end/ui/legacy/fakeCss.css.js');
const stub = sinon.stub(fs, 'existsSync');
stub.callThrough();
stub.withArgs(jsFile).returns(false);
stub.withArgs(cssGenFile).returns(true);
try {
const res = legacyPlugin({
path: './fakeCss.css.js',
importer,
});
assert.deepEqual(res, {
path: cssGenFile,
});
} finally {
stub.restore();
}
});
it('prefers generated TypeScript sources in gen directory over the JS emitted for them', () => {
const importer = path.join(devtoolsRoot, 'front_end/core/sdk/FirstFile.js');
const jsFile = path.join(devtoolsRoot, 'front_end/generated/FakeGenerated.js');
const tsFile = path.join(devtoolsRoot, 'front_end/generated/FakeGenerated.ts');
const genJsFile = path.join(genRoot, 'front_end/generated/FakeGenerated.js');
const genTsFile = path.join(genRoot, 'front_end/generated/FakeGenerated.ts');
const stub = sinon.stub(fs, 'existsSync');
stub.callThrough();
stub.withArgs(jsFile).returns(false);
stub.withArgs(tsFile).returns(false);
stub.withArgs(genJsFile).returns(true);
stub.withArgs(genTsFile).returns(true);
try {
const res = plugin({
path: '../../generated/FakeGenerated.js',
importer,
});
assert.deepEqual(res, {
path: genTsFile,
});
} finally {
stub.restore();
}
});
it('correctly resolves external paths when root is devtools root inside chromium checkout', () => {
const chromiumRoot = '/workspace/chromium/src';
const devtoolsInsideChromiumRoot = path.join(chromiumRoot, 'third_party/devtools-frontend/src');
const genRootDir = path.join(devtoolsInsideChromiumRoot, 'out/Default/gen');
const outDir = path.join(genRootDir, 'front_end/core/sdk');
const customPlugin = esbuildPlugin(outDir, genRootDir, devtoolsInsideChromiumRoot, externalFiles);
assert.deepEqual(
customPlugin({
path: '../common/common.js',
importer: path.join(devtoolsInsideChromiumRoot, 'front_end/core/sdk/FirstFile.js'),
}),
{
path: '../common/common.js',
external: true,
},
);
});
it('correctly resolves external paths when root is chromium checkout root', () => {
const chromiumRoot = '/workspace/chromium/src';
const devtoolsInsideChromiumRoot = path.join(chromiumRoot, 'third_party/devtools-frontend/src');
const genRootDir = path.join(chromiumRoot, 'out/Default/gen');
const outDir = path.join(genRootDir, 'third_party/devtools-frontend/src/front_end/core/sdk');
const customPlugin = esbuildPlugin(outDir, genRootDir, chromiumRoot, externalFiles);
assert.deepEqual(
customPlugin({
path: '../common/common.js',
importer: path.join(devtoolsInsideChromiumRoot, 'front_end/core/sdk/FirstFile.js'),
}),
{
path: '../common/common.js',
external: true,
},
);
});
it('marks differently-named entrypoints (-meta.js) in externalFiles as external', () => {
const timelineOutdir = path.join(genRoot, 'front_end/panels/timeline');
const timelinePlugin = esbuildPlugin(timelineOutdir, genRoot, devtoolsRoot, externalFiles);
assert.deepEqual(
timelinePlugin({
path: './timeline-meta.js',
importer: path.join(devtoolsRoot, 'front_end/panels/timeline/timeline.ts'),
}),
{
path: './timeline-meta.js',
external: true,
},
);
});
it('marks internal module files not in externalFiles as non-external to inline', () => {
const importer = path.join(devtoolsRoot, 'front_end/core/root/root.ts');
const tsFile = path.join(devtoolsRoot, 'front_end/core/root/FakeModule.ts');
const stub = sinon.stub(fs, 'existsSync');
stub.callThrough();
stub.withArgs(tsFile).returns(true);
try {
assert.deepEqual(
plugin({
path: './FakeModule.js',
importer,
}),
{
path: tsFile,
},
);
} finally {
stub.restore();
}
});
it('treats nested prebuilt package files as internal without hardcoded exceptions', () => {
const importer = path.join(devtoolsRoot, 'front_end/third_party/puppeteer/puppeteer.ts');
const pkgFile = path.join(
devtoolsRoot,
'front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js',
);
const stub = sinon.stub(fs, 'existsSync');
stub.callThrough();
stub.withArgs(pkgFile).returns(true);
try {
assert.deepEqual(
plugin({
path: './package/lib/puppeteer/cdp/Browser.js',
importer,
}),
{
path: pkgFile,
},
);
} finally {
stub.restore();
}
});
});