diff --git a/docs/05-plugins.md b/docs/05-plugins.md index 17b3e1ba200..7d3ee34d7a5 100644 --- a/docs/05-plugins.md +++ b/docs/05-plugins.md @@ -195,27 +195,30 @@ If you return an object, then it is possible to resolve an import to a different ```js resolveId(id) { - if (id === 'my-dependency') { - return {id: 'my-dependency-develop', external: true}; - } - return null; + if (id === 'my-dependency') { + return {id: 'my-dependency-develop', external: true}; + } + return null; } ``` -#### `resolveImportMetaUrl` -Type: `({chunkId: string, moduleId: string, format: string}) => string | null`
+#### `resolveImportMeta` +Type: `(property: string | null, {chunkId: string, moduleId: string, format: string}) => string | null`
Kind: `sync, first` -Allows to customize how Rollup handles `import.meta.url`. In ES modules, `import.meta.url` returns the URL of the current module, e.g. `http://server.net/bundle.js` for browsers or `file:///path/to/bundle.js` in Node. +Allows to customize how Rollup handles `import.meta` and `import.meta.someProperty`, in particular `import.meta.url`. In ES modules, `import.meta` is an object and `import.meta.url` contains the URL of the current module, e.g. `http://server.net/bundle.js` for browsers or `file:///path/to/bundle.js` in Node. -By default for formats other than ES modules, Rollup replaces `import.meta.url` with code that attempts to match this behaviour by returning the dynamic URL of the current chunk. Note that all formats except CommonJS and UMD assume that they run in a browser environment where `URL` and `document` are available. +By default for formats other than ES modules, Rollup replaces `import.meta.url` with code that attempts to match this behaviour by returning the dynamic URL of the current chunk. Note that all formats except CommonJS and UMD assume that they run in a browser environment where `URL` and `document` are available. For other properties, `import.meta.someProperty` is replaced with `undefined` while `import.meta` is replaced with an object containing a `url` property. - This behaviour can be changed by returning a replacement for `import.meta.url`. For example, the following code will resolve `import.meta.url` using the relative path of the original module to the current working directory and again resolve this path against the base URL of the current document at runtime: + This behaviour can be changed—also for ES modules—via this hook. For each occurrence of `import.meta<.someProperty>`, this hook is called with the name of the property or `null` if `import.meta` is accessed directly. For example, the following code will resolve `import.meta.url` using the relative path of the original module to the current working directory and again resolve this path against the base URL of the current document at runtime: ```javascript // rollup.config.js -resolveImportMetaUrl({moduleId}) { - return `new URL('${path.relative(process.cwd(), moduleId)}', document.baseURI).href`; +resolveImportMeta(property, {moduleId}) { + if (property === 'url') { + return `new URL('${path.relative(process.cwd(), moduleId)}', document.baseURI).href`; + } + return null; } ``` diff --git a/src/ast/nodes/MetaProperty.ts b/src/ast/nodes/MetaProperty.ts index de0e82d8f9c..e532a8a0adb 100644 --- a/src/ast/nodes/MetaProperty.ts +++ b/src/ast/nodes/MetaProperty.ts @@ -1,9 +1,7 @@ import MagicString from 'magic-string'; import { dirname, normalize, relative } from '../../utils/path'; import { PluginDriver } from '../../utils/pluginDriver'; -import { RenderOptions } from '../../utils/renderHelpers'; import Identifier from './Identifier'; -import Literal from './Literal'; import MemberExpression from './MemberExpression'; import * as NodeType from './NodeType'; import { NodeBase } from './shared/Node'; @@ -35,60 +33,56 @@ const relUrlMechanisms: Record string> = { export default class MetaProperty extends NodeBase { meta: Identifier; property: Identifier; - rendered: boolean; type: NodeType.tMetaProperty; initialise() { if (this.meta.name === 'import') { - this.rendered = false; this.context.addImportMeta(this); } this.included = false; } - render(code: MagicString, options: RenderOptions) { - if (this.meta.name === 'import') this.rendered = true; - super.render(code, options); - } - renderFinalMechanism( code: MagicString, chunkId: string, format: string, pluginDriver: PluginDriver ): boolean { - if (!this.included || !(this.parent instanceof MemberExpression)) return false; - + if (!this.included) return false; const parent = this.parent; - - let importMetaProperty: string; - if (parent.property instanceof Identifier) importMetaProperty = parent.property.name; - else if (parent.property instanceof Literal && typeof parent.property.value === 'string') - importMetaProperty = parent.property.value; - else return false; + const importMetaProperty = + parent instanceof MemberExpression && typeof parent.propertyKey === 'string' + ? parent.propertyKey + : null; // support import.meta.ROLLUP_ASSET_URL_[ID] - if (importMetaProperty.startsWith('ROLLUP_ASSET_URL_')) { + if (importMetaProperty && importMetaProperty.startsWith('ROLLUP_ASSET_URL_')) { const assetFileName = this.context.getAssetFileName(importMetaProperty.substr(17)); const relPath = normalize(relative(dirname(chunkId), assetFileName)); - code.overwrite(parent.start, parent.end, relUrlMechanisms[format](relPath)); + code.overwrite( + (parent as MemberExpression).start, + (parent as MemberExpression).end, + relUrlMechanisms[format](relPath) + ); return true; } - if (importMetaProperty === 'url') { - const replacement = pluginDriver.hookFirstSync('resolveImportMetaUrl', [ - { - chunkId, - format, - moduleId: this.context.module.id - } - ]); - if (typeof replacement === 'string') { + const replacement = pluginDriver.hookFirstSync('resolveImportMeta', [ + importMetaProperty, + { + chunkId, + format, + moduleId: this.context.module.id + } + ]); + if (typeof replacement === 'string') { + if (parent instanceof MemberExpression) { code.overwrite(parent.start, parent.end, replacement); + } else { + code.overwrite(this.start, this.end, replacement); } return true; } - return false; } } diff --git a/src/rollup/types.d.ts b/src/rollup/types.d.ts index 9922f736a71..316666f3c1f 100644 --- a/src/rollup/types.d.ts +++ b/src/rollup/types.d.ts @@ -191,8 +191,9 @@ export type ResolveDynamicImportHook = ( parentId: string ) => Promise | string | void; -export type ResolveImportMetaUrlHook = ( +export type ResolveImportMetaHook = ( this: PluginContext, + prop: string | null, options: { chunkId: string; format: string; moduleId: string } ) => string | void; @@ -249,7 +250,7 @@ export interface Plugin { renderStart?: (this: PluginContext) => Promise | void; resolveDynamicImport?: ResolveDynamicImportHook; resolveId?: ResolveIdHook; - resolveImportMetaUrl?: ResolveImportMetaUrlHook; + resolveImportMeta?: ResolveImportMetaHook; transform?: TransformHook; /** @deprecated */ transformBundle?: TransformChunkHook; diff --git a/src/utils/defaultPlugin.ts b/src/utils/defaultPlugin.ts index a37a30c8c73..4cc1c4bda4f 100644 --- a/src/utils/defaultPlugin.ts +++ b/src/utils/defaultPlugin.ts @@ -14,8 +14,11 @@ export function getRollupDefaultPlugin(options: InputOptions): Plugin { if (typeof specifier === 'string' && !this.isExternal(specifier, parentId, false)) return >this.resolveId(specifier, parentId); }, - resolveImportMetaUrl({ chunkId, format }) { - return importMetaUrlMechanisms[format] && importMetaUrlMechanisms[format](chunkId); + resolveImportMeta(prop, { chunkId, format }) { + const mechanism = importMetaUrlMechanisms[format] && importMetaUrlMechanisms[format](chunkId); + if (mechanism) { + return prop === null ? `({ url: ${mechanism} })` : prop === 'url' ? mechanism : 'undefined'; + } } }; } diff --git a/test/form/samples/configure-import-meta-url/_config.js b/test/form/samples/configure-import-meta-url/_config.js index 2e6888486a8..d0db2f15cc8 100644 --- a/test/form/samples/configure-import-meta-url/_config.js +++ b/test/form/samples/configure-import-meta-url/_config.js @@ -3,9 +3,9 @@ module.exports = { options: { plugins: [ { - resolveImportMetaUrl({ chunkId, moduleId }) { + resolveImportMeta(prop, { chunkId, moduleId }) { if (!moduleId.endsWith('resolved.js')) { - return `'${chunkId}/${moduleId + return `'${prop}=${chunkId}:${moduleId .replace(/\\/g, '/') .split('/') .slice(-2) @@ -15,7 +15,7 @@ module.exports = { } }, { - resolveImportMetaUrl({ moduleId }) { + resolveImportMeta(prop, { moduleId }) { if (!moduleId.endsWith('unresolved.js')) { return `'resolved'`; } diff --git a/test/form/samples/configure-import-meta-url/_expected/amd.js b/test/form/samples/configure-import-meta-url/_expected/amd.js index aa3bae2b300..38d40a1bcb1 100644 --- a/test/form/samples/configure-import-meta-url/_expected/amd.js +++ b/test/form/samples/configure-import-meta-url/_expected/amd.js @@ -1,9 +1,15 @@ define(['module'], function (module) { 'use strict'; console.log('resolved'); + console.log('resolved'); + console.log('resolved'); console.log(new URL(module.uri, document.baseURI).href); + console.log(undefined); + console.log(({ url: new URL(module.uri, document.baseURI).href })); - console.log('amd.js/configure-import-meta-url/main.js'); + console.log('url=amd.js:configure-import-meta-url/main.js'); + console.log('privateProp=amd.js:configure-import-meta-url/main.js'); + console.log('null=amd.js:configure-import-meta-url/main.js'); }); diff --git a/test/form/samples/configure-import-meta-url/_expected/cjs.js b/test/form/samples/configure-import-meta-url/_expected/cjs.js index 40cf325fe1f..f6ab33ad5e5 100644 --- a/test/form/samples/configure-import-meta-url/_expected/cjs.js +++ b/test/form/samples/configure-import-meta-url/_expected/cjs.js @@ -1,7 +1,13 @@ 'use strict'; +console.log('resolved'); +console.log('resolved'); console.log('resolved'); console.log((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('cjs.js', document.baseURI).href))); +console.log(undefined); +console.log(({ url: (typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('cjs.js', document.baseURI).href)) })); -console.log('cjs.js/configure-import-meta-url/main.js'); +console.log('url=cjs.js:configure-import-meta-url/main.js'); +console.log('privateProp=cjs.js:configure-import-meta-url/main.js'); +console.log('null=cjs.js:configure-import-meta-url/main.js'); diff --git a/test/form/samples/configure-import-meta-url/_expected/es.js b/test/form/samples/configure-import-meta-url/_expected/es.js index 9c60ac1b8c3..65363fbdc91 100644 --- a/test/form/samples/configure-import-meta-url/_expected/es.js +++ b/test/form/samples/configure-import-meta-url/_expected/es.js @@ -1,5 +1,11 @@ console.log('resolved'); +console.log('resolved'); +console.log('resolved'); console.log(import.meta.url); +console.log(import.meta.privateProp); +console.log(import.meta); -console.log('es.js/configure-import-meta-url/main.js'); +console.log('url=es.js:configure-import-meta-url/main.js'); +console.log('privateProp=es.js:configure-import-meta-url/main.js'); +console.log('null=es.js:configure-import-meta-url/main.js'); diff --git a/test/form/samples/configure-import-meta-url/_expected/iife.js b/test/form/samples/configure-import-meta-url/_expected/iife.js index 885ab9f917e..2914b6951c8 100644 --- a/test/form/samples/configure-import-meta-url/_expected/iife.js +++ b/test/form/samples/configure-import-meta-url/_expected/iife.js @@ -1,10 +1,16 @@ (function () { 'use strict'; + console.log('resolved'); + console.log('resolved'); console.log('resolved'); console.log((document.currentScript && document.currentScript.src || new URL('iife.js', document.baseURI).href)); + console.log(undefined); + console.log(({ url: (document.currentScript && document.currentScript.src || new URL('iife.js', document.baseURI).href) })); - console.log('iife.js/configure-import-meta-url/main.js'); + console.log('url=iife.js:configure-import-meta-url/main.js'); + console.log('privateProp=iife.js:configure-import-meta-url/main.js'); + console.log('null=iife.js:configure-import-meta-url/main.js'); }()); diff --git a/test/form/samples/configure-import-meta-url/_expected/system.js b/test/form/samples/configure-import-meta-url/_expected/system.js index f6fea59b8e9..0023ad18505 100644 --- a/test/form/samples/configure-import-meta-url/_expected/system.js +++ b/test/form/samples/configure-import-meta-url/_expected/system.js @@ -4,10 +4,16 @@ System.register([], function (exports, module) { execute: function () { console.log('resolved'); + console.log('resolved'); + console.log('resolved'); console.log(module.meta.url); + console.log(undefined); + console.log(({ url: module.meta.url })); - console.log('system.js/configure-import-meta-url/main.js'); + console.log('url=system.js:configure-import-meta-url/main.js'); + console.log('privateProp=system.js:configure-import-meta-url/main.js'); + console.log('null=system.js:configure-import-meta-url/main.js'); } }; diff --git a/test/form/samples/configure-import-meta-url/_expected/umd.js b/test/form/samples/configure-import-meta-url/_expected/umd.js index 4fac39ab8db..187f2c2e07e 100644 --- a/test/form/samples/configure-import-meta-url/_expected/umd.js +++ b/test/form/samples/configure-import-meta-url/_expected/umd.js @@ -4,9 +4,15 @@ }(function () { 'use strict'; console.log('resolved'); + console.log('resolved'); + console.log('resolved'); console.log((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('umd.js', document.baseURI).href))); + console.log(undefined); + console.log(({ url: (typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('umd.js', document.baseURI).href)) })); - console.log('umd.js/configure-import-meta-url/main.js'); + console.log('url=umd.js:configure-import-meta-url/main.js'); + console.log('privateProp=umd.js:configure-import-meta-url/main.js'); + console.log('null=umd.js:configure-import-meta-url/main.js'); })); diff --git a/test/form/samples/configure-import-meta-url/main.js b/test/form/samples/configure-import-meta-url/main.js index 31d82d63ea7..a4a0af99305 100644 --- a/test/form/samples/configure-import-meta-url/main.js +++ b/test/form/samples/configure-import-meta-url/main.js @@ -2,3 +2,5 @@ import './resolved'; import './unresolved'; console.log(import.meta.url); +console.log(import.meta.privateProp); +console.log(import.meta); diff --git a/test/form/samples/configure-import-meta-url/resolved.js b/test/form/samples/configure-import-meta-url/resolved.js index d9536a69b3f..0a555e5ad35 100644 --- a/test/form/samples/configure-import-meta-url/resolved.js +++ b/test/form/samples/configure-import-meta-url/resolved.js @@ -1 +1,3 @@ console.log(import.meta.url); +console.log(import.meta.privateProp); +console.log(import.meta); diff --git a/test/form/samples/configure-import-meta-url/unresolved.js b/test/form/samples/configure-import-meta-url/unresolved.js index d9536a69b3f..0a555e5ad35 100644 --- a/test/form/samples/configure-import-meta-url/unresolved.js +++ b/test/form/samples/configure-import-meta-url/unresolved.js @@ -1 +1,3 @@ console.log(import.meta.url); +console.log(import.meta.privateProp); +console.log(import.meta);