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
11 changes: 8 additions & 3 deletions .github/workflows/build-and-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,14 @@ jobs:
cross: zig
- host: ubuntu-latest
target: armv7-linux-androideabi
cross: zig
cross: napi
# There are compile issues when using the cache with Android
cache-cargo: false
- host: ubuntu-latest
target: aarch64-linux-android
cross: zig
cross: napi
# There are compile issues when using the cache with Android
cache-cargo: false
- host: ubuntu-latest
target: riscv64gc-unknown-linux-gnu
setup: |
Expand Down Expand Up @@ -210,6 +214,7 @@ jobs:
uses: openharmony-rs/setup-ohos-sdk@52d50de65363f895558a43de0dceb1f8e3679b1c # v0.2.3
- name: Restore Cargo cache
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
if: matrix.settings.cache-cargo != false
with:
path: |
~/.cargo/registry/index/
Expand Down Expand Up @@ -258,7 +263,7 @@ jobs:
if: ${{ !matrix.settings.docker && !matrix.settings.build }}
shell: bash
- name: Save Cargo cache
if: github.ref == 'refs/heads/master'
if: github.ref == 'refs/heads/master' && matrix.settings.cache-cargo != false
uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: |
Expand Down
13 changes: 12 additions & 1 deletion docs/plugin-development/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1427,7 +1427,7 @@ interface EmittedAsset {

当生成代码块或者资源文件时,可以提供 `name` 或者 `fileName`。如果提供了 `fileName`,它将不会被修改,而是直接作为生成文件的名称,如果这样会导致冲突,则会抛出错误。否则,如果提供了 `name`,它将被用作对应的 [`output.chunkFileNames`](../configuration-options/index.md#output-chunkfilenames) 或者 [`output.assetFileNames`](../configuration-options/index.md#output-assetfilenames) 模式中的 `[name]` 的替换,可能会在文件名的末尾添加一个唯一的数字,以避免冲突。如果既没有提供 `name` 也没有提供 `fileName`,则会使用默认名称。预构建的代码块必须始终有一个 `fileName`。

你可以通过 `import.meta.ROLLUP_FILE_URL_referenceId` 在任何由 [`load`](#load) 或 [`transform`](#transform) 插件钩子返回的代码中引用生成的文件的 URL。有关更多详细信息和示例,请参见 [File URL](#file-urls)。
你可以通过 `import.meta.ROLLUP_FILE_URL_referenceId`(返回一个字符串)或 `import.meta.ROLLUP_FILE_URL_OBJ_referenceId`(返回一个 URL 对象),在任何由 [`load`](#load) 或 [`transform`](#transform) 插件钩子返回的代码中引用生成的文件的 URL。有关更多详细信息和示例,请参见 [File URL](#file-urls)。

替换 `import.meta.ROLLUP_FILE_URL_referenceId` 的生成代码可以通过 [`resolveFileUrl`](#resolvefileurl) 插件钩子进行自定义。你也可以使用 [`this.getFileName(referenceId)`](#this-getfilename) 在文件名可用时确定文件名。如果没有显式设置文件名,则

Expand Down Expand Up @@ -2123,6 +2123,17 @@ export const size = 6;

如果你构建上诉代码,则主块和工作块都将通过共享块从 `config.js` 共享代码。这使我们能够利用浏览器缓存来减少传输数据并加快加载工作块的速度。

你也可以使用 `import.meta.ROLLUP_FILE_URL_OBJ_referenceId` 直接获取一个 URL 对象,而不是一个字符串。当你需要 URL 对象本身时,这会更高效,因为它避免了重复创建对象:

```js
// 使用 ROLLUP_FILE_URL(返回字符串,需要通过 new URL() 包装使用)
const urlString = import.meta.ROLLUP_FILE_URL_referenceId;
const urlObject = new URL(urlString);

// 使用 ROLLUP_FILE_URL_OBJ(直接返回 URL 对象)
const urlObject = import.meta.ROLLUP_FILE_URL_OBJ_referenceId;
```

## 转换器 {#Transformers}

转换器插件(即返回 `transform` 函数以进行例如转换非 JS 文件的转换器)应支持 `options.include` 和 `options.exclude`,两者都可以是 minimatch 模式或 minimatch 模式数组。如果省略 `options.include` 或其长度为零,则默认情况下应包括文件;否则,只有 ID 与其中一个模式匹配时才应包括它们。
Expand Down
74 changes: 42 additions & 32 deletions src/ast/nodes/MetaProperty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type * as NodeType from './NodeType';
import { NodeBase } from './shared/Node';

const FILE_PREFIX = 'ROLLUP_FILE_URL_';
const FILE_OBJ_PREFIX = 'ROLLUP_FILE_URL_OBJ_';
const IMPORT = 'import';

export default class MetaProperty extends NodeBase {
Expand All @@ -32,8 +33,12 @@ export default class MetaProperty extends NodeBase {
meta: { name },
metaProperty
} = this;
if (name === IMPORT && metaProperty?.startsWith(FILE_PREFIX)) {
return outputPluginDriver.getFileName(metaProperty.slice(FILE_PREFIX.length));
if (name === IMPORT) {
if (metaProperty?.startsWith(FILE_OBJ_PREFIX)) {
return outputPluginDriver.getFileName(metaProperty.slice(FILE_OBJ_PREFIX.length));
} else if (metaProperty?.startsWith(FILE_PREFIX)) {
return outputPluginDriver.getFileName(metaProperty.slice(FILE_PREFIX.length));
}
}
return null;
}
Expand All @@ -59,7 +64,9 @@ export default class MetaProperty extends NodeBase {
parent instanceof MemberExpression && typeof parent.propertyKey === 'string'
? parent.propertyKey
: null);
if (metaProperty?.startsWith(FILE_PREFIX)) {
if (metaProperty?.startsWith(FILE_OBJ_PREFIX)) {
this.referenceId = metaProperty.slice(FILE_OBJ_PREFIX.length);
} else if (metaProperty?.startsWith(FILE_PREFIX)) {
this.referenceId = metaProperty.slice(FILE_PREFIX.length);
}
}
Expand Down Expand Up @@ -87,10 +94,11 @@ export default class MetaProperty extends NodeBase {
if (referenceId) {
const fileName = pluginDriver.getFileName(referenceId);
const relativePath = normalize(relative(dirname(chunkId), fileName));
const isUrlObject = !!metaProperty?.startsWith(FILE_OBJ_PREFIX);
const replacement =
pluginDriver.hookFirstSync('resolveFileUrl', [
{ chunkId, fileName, format, moduleId, referenceId, relativePath }
]) || relativeUrlMechanisms[format](relativePath);
]) || relativeUrlMechanisms[format](relativePath, isUrlObject);

code.overwrite(
(parent as MemberExpression).start,
Expand Down Expand Up @@ -126,7 +134,9 @@ export default class MetaProperty extends NodeBase {
): void {
this.preliminaryChunkId = preliminaryChunkId;
const accessedGlobals = (
this.metaProperty?.startsWith(FILE_PREFIX) ? accessedFileUrlGlobals : accessedMetaUrlGlobals
this.metaProperty?.startsWith(FILE_PREFIX) || this.metaProperty?.startsWith(FILE_OBJ_PREFIX)
? accessedFileUrlGlobals
: accessedMetaUrlGlobals
)[format];
if (accessedGlobals.length > 0) {
this.scope.addAccessedGlobals(accessedGlobals, accessedGlobalsByScope);
Expand Down Expand Up @@ -154,13 +164,15 @@ const accessedFileUrlGlobals = {
umd: ['document', 'require', 'URL']
};

const getResolveUrl = (path: string, URL = 'URL') => `new ${URL}(${path}).href`;
const getResolveUrl = (path: string, asObject: boolean, URL = 'URL') =>
`new ${URL}(${path})${asObject ? '' : '.href'}`;

const getRelativeUrlFromDocument = (relativePath: string, umd = false) =>
const getRelativeUrlFromDocument = (relativePath: string, asObject: boolean, umd = false) =>
getResolveUrl(
`'${escapeId(relativePath)}', ${
umd ? `typeof document === 'undefined' ? location.href : ` : ''
}document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || document.baseURI`
}document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || document.baseURI`,
asObject
);

const getGenericImportMetaMechanism =
Expand All @@ -174,10 +186,11 @@ const getGenericImportMetaMechanism =
: 'undefined';
};

const getFileUrlFromFullPath = (path: string) => `require('u' + 'rl').pathToFileURL(${path}).href`;
const getFileUrlFromFullPath = (path: string, asObject: boolean) =>
`require('u' + 'rl').pathToFileURL(${path})${asObject ? '' : '.href'}`;

const getFileUrlFromRelativePath = (path: string) =>
getFileUrlFromFullPath(`__dirname + '/${escapeId(path)}'`);
const getFileUrlFromRelativePath = (path: string, asObject: boolean) =>
getFileUrlFromFullPath(`__dirname + '/${escapeId(path)}'`, asObject);

const getUrlFromDocument = (chunkId: string, umd = false) =>
`${
Expand All @@ -186,42 +199,39 @@ const getUrlFromDocument = (chunkId: string, umd = false) =>
chunkId
)}', document.baseURI).href)`;

const relativeUrlMechanisms: Record<InternalModuleFormat, (relativePath: string) => string> = {
amd: relativePath => {
const relativeUrlMechanisms: Record<
InternalModuleFormat,
(relativePath: string, asObject: boolean) => string
> = {
amd: (relativePath, asObject: boolean) => {
if (relativePath[0] !== '.') relativePath = './' + relativePath;
return getResolveUrl(`require.toUrl('${escapeId(relativePath)}'), document.baseURI`);
return getResolveUrl(`require.toUrl('${escapeId(relativePath)}'), document.baseURI`, asObject);
},
cjs: relativePath =>
`(typeof document === 'undefined' ? ${getFileUrlFromRelativePath(
relativePath
)} : ${getRelativeUrlFromDocument(relativePath)})`,
es: relativePath => getResolveUrl(`'${escapeId(relativePath)}', import.meta.url`),
iife: relativePath => getRelativeUrlFromDocument(relativePath),
system: relativePath => getResolveUrl(`'${escapeId(relativePath)}', module.meta.url`),
umd: relativePath =>
`(typeof document === 'undefined' && typeof location === 'undefined' ? ${getFileUrlFromRelativePath(
relativePath
)} : ${getRelativeUrlFromDocument(relativePath, true)})`
cjs: (relativePath, asObject: boolean) =>
`(typeof document === 'undefined' ? ${getFileUrlFromRelativePath(relativePath, asObject)} : ${getRelativeUrlFromDocument(relativePath, asObject)})`,
es: (relativePath, asObject: boolean) =>
getResolveUrl(`'${escapeId(relativePath)}', import.meta.url`, asObject),
iife: (relativePath, asObject: boolean) => getRelativeUrlFromDocument(relativePath, asObject),
system: (relativePath, asObject: boolean) =>
getResolveUrl(`'${escapeId(relativePath)}', module.meta.url`, asObject),
umd: (relativePath, asObject: boolean) =>
`(typeof document === 'undefined' && typeof location === 'undefined' ? ${getFileUrlFromRelativePath(relativePath, asObject)} : ${getRelativeUrlFromDocument(relativePath, asObject, true)})`
};

const importMetaMechanisms: Record<
string,
(property: string | null, options: { chunkId: string; snippets: GenerateCodeSnippets }) => string
> = {
amd: getGenericImportMetaMechanism(() => getResolveUrl(`module.uri, document.baseURI`)),
amd: getGenericImportMetaMechanism(() => getResolveUrl(`module.uri, document.baseURI`, false)),
cjs: getGenericImportMetaMechanism(
chunkId =>
`(typeof document === 'undefined' ? ${getFileUrlFromFullPath(
'__filename'
)} : ${getUrlFromDocument(chunkId)})`
`(typeof document === 'undefined' ? ${getFileUrlFromFullPath('__filename', false)} : ${getUrlFromDocument(chunkId)})`
),
iife: getGenericImportMetaMechanism(chunkId => getUrlFromDocument(chunkId)),
system: (property, { snippets: { getPropertyAccess } }) =>
property === null ? `module.meta` : `module.meta${getPropertyAccess(property)}`,
umd: getGenericImportMetaMechanism(
chunkId =>
`(typeof document === 'undefined' && typeof location === 'undefined' ? ${getFileUrlFromFullPath(
'__filename'
)} : ${getUrlFromDocument(chunkId, true)})`
`(typeof document === 'undefined' && typeof location === 'undefined' ? ${getFileUrlFromFullPath('__filename', false)} : ${getUrlFromDocument(chunkId, true)})`
)
};
29 changes: 29 additions & 0 deletions test/chunking-form/samples/resolve-file-url-obj/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module.exports = defineTest({
description: 'allows to use ROLLUP_FILE_URL_OBJ to get URL objects directly',
options: {
plugins: [
{
resolveId(id) {
if (id === 'url-test') {
return id;
}
},
load(id) {
if (id === 'url-test') {
const assetId = this.emitFile({
type: 'asset',
name: 'test.txt',
source: 'test content'
});
return `
// Test string URL replacement
export const assetString = import.meta.ROLLUP_FILE_URL_${assetId};
// Test URL object replacement
export const assetObject = import.meta.ROLLUP_FILE_URL_OBJ_${assetId};
`;
}
}
}
]
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test content
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
define(['require'], (function (require) { 'use strict';

// Test string URL replacement
const assetString = new URL(require.toUrl('./assets/test-r6af3lUh.txt'), document.baseURI).href;
// Test URL object replacement
const assetObject = new URL(require.toUrl('./assets/test-r6af3lUh.txt'), document.baseURI);

console.log('String URL:', assetString);
console.log('URL Object:', assetObject);

}));
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test content
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';

// Test string URL replacement
const assetString = (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__dirname + '/assets/test-r6af3lUh.txt').href : new URL('assets/test-r6af3lUh.txt', document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || document.baseURI).href);
// Test URL object replacement
const assetObject = (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__dirname + '/assets/test-r6af3lUh.txt') : new URL('assets/test-r6af3lUh.txt', document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || document.baseURI));

console.log('String URL:', assetString);
console.log('URL Object:', assetObject);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test content
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Test string URL replacement
const assetString = new URL('assets/test-r6af3lUh.txt', import.meta.url).href;
// Test URL object replacement
const assetObject = new URL('assets/test-r6af3lUh.txt', import.meta.url);

console.log('String URL:', assetString);
console.log('URL Object:', assetObject);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test content
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
System.register([], (function (exports, module) {
'use strict';
return {
execute: (function () {

// Test string URL replacement
const assetString = new URL('assets/test-r6af3lUh.txt', module.meta.url).href;
// Test URL object replacement
const assetObject = new URL('assets/test-r6af3lUh.txt', module.meta.url);

console.log('String URL:', assetString);
console.log('URL Object:', assetObject);

})
};
}));
4 changes: 4 additions & 0 deletions test/chunking-form/samples/resolve-file-url-obj/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { assetString, assetObject } from 'url-test';

console.log('String URL:', assetString);
console.log('URL Object:', assetObject);
Loading