mirror of
https://github.com/jlengrand/open-wc.git
synced 2026-03-10 08:31:19 +00:00
fix(es-dev-server): don't rely on serverStart for built-in plugins
This commit is contained in:
@@ -62,6 +62,9 @@ export function createMiddlewares(
|
||||
watch,
|
||||
logErrorsToBrowser,
|
||||
watchDebounce,
|
||||
babelExclude,
|
||||
babelModernExclude,
|
||||
babelModuleExclude,
|
||||
} = config;
|
||||
|
||||
const middlewares: Middleware[] = [];
|
||||
@@ -90,22 +93,40 @@ export function createMiddlewares(
|
||||
const setupMessageChanel = watch || (logErrorsToBrowser && (setupCompatibility || nodeResolve));
|
||||
|
||||
if (fileExtensions.length > 0) {
|
||||
plugins.unshift(fileExtensionsPlugin());
|
||||
plugins.unshift(fileExtensionsPlugin({ fileExtensions }));
|
||||
}
|
||||
|
||||
if (nodeResolve || hasHook(plugins, 'resolveId')) {
|
||||
plugins.push(resolveModuleImportsPlugin());
|
||||
if (nodeResolve) {
|
||||
plugins.push(nodeResolvePlugin());
|
||||
plugins.push(nodeResolvePlugin({ rootDir, fileExtensions, nodeResolve }));
|
||||
}
|
||||
plugins.push(resolveModuleImportsPlugin({ rootDir, plugins }));
|
||||
}
|
||||
|
||||
if (setupCompatibility || setupBabel) {
|
||||
plugins.push(babelTransformPlugin());
|
||||
plugins.push(
|
||||
babelTransformPlugin({
|
||||
rootDir,
|
||||
readUserBabelConfig,
|
||||
nodeResolve,
|
||||
compatibilityMode,
|
||||
customBabelConfig,
|
||||
fileExtensions,
|
||||
babelExclude,
|
||||
babelModernExclude,
|
||||
babelModuleExclude,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (polyfillsLoader && setupCompatibility) {
|
||||
plugins.push(polyfillsLoaderPlugin());
|
||||
plugins.push(
|
||||
polyfillsLoaderPlugin({
|
||||
rootDir,
|
||||
compatibilityMode,
|
||||
polyfillsLoaderConfig,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// strips a base path from requests
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable no-console */
|
||||
import { Options } from '@rollup/plugin-node-resolve';
|
||||
import { Plugin } from '../Plugin';
|
||||
import { createCompatibilityTransform, TransformJs } from '../utils/compatibility-transform';
|
||||
import { getUserAgentCompat } from '../utils/user-agent-compat';
|
||||
@@ -10,14 +11,27 @@ import { parse as parseHtml, serialize as serializeHtml } from 'parse5';
|
||||
import { URL, pathToFileURL, fileURLToPath } from 'url';
|
||||
import { Context } from 'koa';
|
||||
import { isPolyfill } from '../utils/utils';
|
||||
import { TransformOptions } from '@babel/core';
|
||||
|
||||
function createFilePath(context: Context, rootDir: string) {
|
||||
return fileURLToPath(new URL(`.${context.path}`, `${pathToFileURL(rootDir)}/`));
|
||||
}
|
||||
|
||||
export function babelTransformPlugin(): Plugin {
|
||||
let rootDir: string;
|
||||
let compatibilityTransform: TransformJs;
|
||||
interface BabelTransformConfig {
|
||||
rootDir: string;
|
||||
readUserBabelConfig: boolean;
|
||||
nodeResolve: boolean | Options;
|
||||
compatibilityMode: string;
|
||||
customBabelConfig?: TransformOptions;
|
||||
fileExtensions: string[];
|
||||
babelExclude: string[];
|
||||
babelModernExclude: string[];
|
||||
babelModuleExclude: string[];
|
||||
}
|
||||
|
||||
export function babelTransformPlugin(config: BabelTransformConfig): Plugin {
|
||||
const compatibilityTransform = createCompatibilityTransform(config);
|
||||
const { rootDir } = config;
|
||||
|
||||
async function transformJs(context: Context, code: string) {
|
||||
const filePath = createFilePath(context, rootDir);
|
||||
@@ -32,11 +46,6 @@ export function babelTransformPlugin(): Plugin {
|
||||
}
|
||||
|
||||
return {
|
||||
serverStart({ config }) {
|
||||
({ rootDir } = config);
|
||||
compatibilityTransform = createCompatibilityTransform(config);
|
||||
},
|
||||
|
||||
async transform(context) {
|
||||
// transform a single file
|
||||
if (context.response.is('js') && !isPolyfill(context.url)) {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Plugin } from '../Plugin';
|
||||
|
||||
interface FileExtensionsConfig {
|
||||
fileExtensions: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin which serves configured file extensions as JS.
|
||||
*/
|
||||
export function fileExtensionsPlugin(): Plugin {
|
||||
let fileExtensions: string[];
|
||||
export function fileExtensionsPlugin(config: FileExtensionsConfig): Plugin {
|
||||
const { fileExtensions } = config;
|
||||
|
||||
return {
|
||||
serverStart({ config }) {
|
||||
fileExtensions = config.fileExtensions.map(ext => (ext.startsWith('.') ? ext : `.${ext}`));
|
||||
},
|
||||
|
||||
resolveMimeType(context) {
|
||||
if (fileExtensions.some(ext => context.path.endsWith(ext))) {
|
||||
return 'js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import createRollupResolve from '@rollup/plugin-node-resolve';
|
||||
import createRollupResolve, { Options } from '@rollup/plugin-node-resolve';
|
||||
import { Plugin as RollupPlugin } from 'rollup';
|
||||
import path from 'path';
|
||||
import { URL, pathToFileURL, fileURLToPath } from 'url';
|
||||
@@ -17,27 +17,29 @@ const fakePluginContext = {
|
||||
},
|
||||
};
|
||||
|
||||
export function nodeResolvePlugin(): Plugin {
|
||||
let fileExtensions: string[];
|
||||
let rootDir: string;
|
||||
let nodeResolve: RollupPlugin;
|
||||
interface NodeResolveConfig {
|
||||
rootDir: string;
|
||||
fileExtensions: string[];
|
||||
nodeResolve: boolean | Options;
|
||||
}
|
||||
|
||||
export function nodeResolvePlugin(config: NodeResolveConfig): Plugin {
|
||||
const { fileExtensions, rootDir } = config;
|
||||
const options = {
|
||||
rootDir,
|
||||
// allow resolving polyfills for nodejs libs
|
||||
preferBuiltins: false,
|
||||
extensions: fileExtensions,
|
||||
...(typeof config.nodeResolve === 'object' ? config.nodeResolve : {}),
|
||||
};
|
||||
const nodeResolve = createRollupResolve(options);
|
||||
|
||||
// call buildStart
|
||||
const preserveSymlinks = options?.customResolveOptions?.preserveSymlinks;
|
||||
nodeResolve.buildStart?.call(fakePluginContext as any, { preserveSymlinks });
|
||||
|
||||
return {
|
||||
async serverStart({ config }) {
|
||||
({ rootDir, fileExtensions } = config);
|
||||
const options = {
|
||||
rootDir,
|
||||
// allow resolving polyfills for nodejs libs
|
||||
preferBuiltins: false,
|
||||
extensions: fileExtensions,
|
||||
...(typeof config.nodeResolve === 'object' ? config.nodeResolve : {}),
|
||||
};
|
||||
nodeResolve = createRollupResolve(options);
|
||||
|
||||
// call buildStart
|
||||
const preserveSymlinks = options?.customResolveOptions?.preserveSymlinks;
|
||||
nodeResolve.buildStart?.call(fakePluginContext as any, { preserveSymlinks });
|
||||
},
|
||||
async serverStart({ config }) {},
|
||||
|
||||
async resolveImport({ source, context }) {
|
||||
if (whatwgUrl.parseURL(source) != null) {
|
||||
|
||||
@@ -13,27 +13,24 @@ interface IndexHTMLData {
|
||||
inlineScripts: GeneratedFile[];
|
||||
}
|
||||
|
||||
export interface PolyfillsLoaderMiddlewareConfig {}
|
||||
export interface PolyfillsLoaderPluginConfig {
|
||||
rootDir: string;
|
||||
compatibilityMode: string;
|
||||
polyfillsLoaderConfig?: Partial<PolyfillsLoaderConfig>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates plugin which injects polyfills and code into HTML pages which allows
|
||||
* it to run on legacy browsers.
|
||||
*/
|
||||
export function polyfillsLoaderPlugin(): Plugin {
|
||||
export function polyfillsLoaderPlugin(config: PolyfillsLoaderPluginConfig): Plugin {
|
||||
const { compatibilityMode, rootDir, polyfillsLoaderConfig = {} } = config;
|
||||
// index html data, keyed by url
|
||||
const indexHTMLData = new Map<string, IndexHTMLData>();
|
||||
// polyfills, keyed by request path
|
||||
const polyfills = new Map<string, string>();
|
||||
|
||||
let compatibilityMode: string;
|
||||
let rootDir: string;
|
||||
let polyfillsLoaderConfig: Partial<PolyfillsLoaderConfig>;
|
||||
|
||||
return {
|
||||
serverStart({ config }) {
|
||||
({ compatibilityMode, rootDir, polyfillsLoaderConfig = {} } = config);
|
||||
},
|
||||
|
||||
async serve(context) {
|
||||
const uaCompat = getUserAgentCompat(context);
|
||||
|
||||
|
||||
@@ -186,16 +186,16 @@ async function resolveWithPluginHooks(
|
||||
return resolveModuleImports(jsCode, filePath, resolveImport);
|
||||
}
|
||||
|
||||
export function resolveModuleImportsPlugin(): Plugin {
|
||||
let rootDir: string;
|
||||
let resolvePlugins: Plugin[];
|
||||
export interface ResolveModuleImportsPlugin {
|
||||
rootDir: string;
|
||||
plugins?: Plugin[];
|
||||
}
|
||||
|
||||
export function resolveModuleImportsPlugin(config: ResolveModuleImportsPlugin): Plugin {
|
||||
const { rootDir, plugins = [] } = config;
|
||||
const resolvePlugins = plugins.filter(pl => !!pl.resolveImport);
|
||||
|
||||
return {
|
||||
serverStart({ config }) {
|
||||
({ rootDir } = config);
|
||||
resolvePlugins = config.plugins.filter(pl => !!pl.resolveImport);
|
||||
},
|
||||
|
||||
async transform(context) {
|
||||
if (resolvePlugins.length === 0) {
|
||||
return;
|
||||
|
||||
@@ -192,21 +192,22 @@ const host = 'http://localhost:8080/';
|
||||
|
||||
describe('resolveModuleImportsPlugin', () => {
|
||||
it('lets plugins resolve imports using the resolveImport hook', async () => {
|
||||
const rootDir = path.resolve(__dirname, '..', 'fixtures', 'simple');
|
||||
const plugins: Plugin[] = [
|
||||
{
|
||||
resolveImport({ source }) {
|
||||
return `RESOLVED__${source}`;
|
||||
},
|
||||
},
|
||||
resolveModuleImportsPlugin(),
|
||||
];
|
||||
plugins.push(resolveModuleImportsPlugin({ rootDir, plugins }));
|
||||
|
||||
let server;
|
||||
try {
|
||||
({ server } = await startServer(
|
||||
createConfig({
|
||||
port: 8080,
|
||||
rootDir: path.resolve(__dirname, '..', 'fixtures', 'simple'),
|
||||
rootDir,
|
||||
compatibility: 'none',
|
||||
plugins,
|
||||
}),
|
||||
@@ -223,21 +224,22 @@ describe('resolveModuleImportsPlugin', () => {
|
||||
});
|
||||
|
||||
it('resolved imports in inline modules in HTML files', async () => {
|
||||
const rootDir = path.resolve(__dirname, '..', 'fixtures', 'simple');
|
||||
const plugins: Plugin[] = [
|
||||
{
|
||||
resolveImport({ source }) {
|
||||
return `RESOLVED__${source}`;
|
||||
},
|
||||
},
|
||||
resolveModuleImportsPlugin(),
|
||||
];
|
||||
plugins.push(resolveModuleImportsPlugin({ rootDir, plugins }));
|
||||
|
||||
let server;
|
||||
try {
|
||||
({ server } = await startServer(
|
||||
createConfig({
|
||||
port: 8080,
|
||||
rootDir: path.resolve(__dirname, '..', 'fixtures', 'simple'),
|
||||
rootDir,
|
||||
compatibility: 'none',
|
||||
plugins,
|
||||
}),
|
||||
@@ -254,21 +256,22 @@ describe('resolveModuleImportsPlugin', () => {
|
||||
});
|
||||
|
||||
it('unmatched resolve leaves import untouched', async () => {
|
||||
const rootDir = path.resolve(__dirname, '..', 'fixtures', 'simple');
|
||||
const plugins: Plugin[] = [
|
||||
{
|
||||
resolveImport() {
|
||||
return undefined;
|
||||
},
|
||||
},
|
||||
resolveModuleImportsPlugin(),
|
||||
];
|
||||
plugins.push(resolveModuleImportsPlugin({ rootDir, plugins }));
|
||||
|
||||
let server;
|
||||
try {
|
||||
({ server } = await startServer(
|
||||
createConfig({
|
||||
port: 8080,
|
||||
rootDir: path.resolve(__dirname, '..', 'fixtures', 'simple'),
|
||||
rootDir,
|
||||
compatibility: 'none',
|
||||
plugins,
|
||||
}),
|
||||
@@ -285,6 +288,7 @@ describe('resolveModuleImportsPlugin', () => {
|
||||
});
|
||||
|
||||
it('first matching plugin takes priority', async () => {
|
||||
const rootDir = path.resolve(__dirname, '..', 'fixtures', 'simple');
|
||||
const plugins: Plugin[] = [
|
||||
{
|
||||
resolveImport({ source, context }) {
|
||||
@@ -298,15 +302,15 @@ describe('resolveModuleImportsPlugin', () => {
|
||||
return `RESOLVED__B__${source}`;
|
||||
},
|
||||
},
|
||||
resolveModuleImportsPlugin(),
|
||||
];
|
||||
plugins.push(resolveModuleImportsPlugin({ rootDir, plugins }));
|
||||
|
||||
let server;
|
||||
try {
|
||||
({ server } = await startServer(
|
||||
createConfig({
|
||||
port: 8080,
|
||||
rootDir: path.resolve(__dirname, '..', 'fixtures', 'simple'),
|
||||
rootDir,
|
||||
compatibility: 'none',
|
||||
plugins,
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user