mirror of
https://github.com/idanoo/GoScrobble.git
synced 2024-11-22 16:35:14 +00:00
194 lines
6.4 KiB
JavaScript
194 lines
6.4 KiB
JavaScript
|
/**
|
|||
|
* Copyright 2018 Google Inc. All Rights Reserved.
|
|||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|||
|
* you may not use this file except in compliance with the License.
|
|||
|
* You may obtain a copy of the License at
|
|||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
|
* Unless required by applicable law or agreed to in writing, software
|
|||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
|
* See the License for the specific language governing permissions and
|
|||
|
* limitations under the License.
|
|||
|
*/
|
|||
|
|
|||
|
const { readFileSync } = require("fs");
|
|||
|
const { join } = require("path");
|
|||
|
const ejs = require("ejs");
|
|||
|
const MagicString = require("magic-string");
|
|||
|
|
|||
|
const defaultOpts = {
|
|||
|
// A string containing the EJS template for the amd loader. If `undefined`,
|
|||
|
// OMT will use `loader.ejs`.
|
|||
|
loader: readFileSync(join(__dirname, "/loader.ejs"), "utf8"),
|
|||
|
// Use `fetch()` + `eval()` to load dependencies instead of `<script>` tags
|
|||
|
// and `importScripts()`. _This is not CSP compliant, but is required if you
|
|||
|
// want to use dynamic imports in ServiceWorker_.
|
|||
|
useEval: false,
|
|||
|
// A RegExp to find `new Workers()` calls. The second capture group _must_
|
|||
|
// capture the provided file name without the quotes.
|
|||
|
workerRegexp: /new Worker\((["'])(.+?)\1(,[^)]+)?\)/g,
|
|||
|
// Function name to use instead of AMD’s `define`.
|
|||
|
amdFunctionName: "define",
|
|||
|
// A function that determines whether the loader code should be prepended to a
|
|||
|
// certain chunk. Should return true if the load is supposed to be prepended.
|
|||
|
prependLoader: (chunk, workerFiles) =>
|
|||
|
chunk.isEntry || workerFiles.includes(chunk.facadeModuleId),
|
|||
|
// The scheme used when importing workers as a URL.
|
|||
|
urlLoaderScheme: "omt",
|
|||
|
// Silence the warning about ESM being badly supported in workers.
|
|||
|
silenceESMWorkerWarning: false,
|
|||
|
};
|
|||
|
|
|||
|
module.exports = function(opts = {}) {
|
|||
|
opts = Object.assign({}, defaultOpts, opts);
|
|||
|
|
|||
|
opts.loader = ejs.render(opts.loader, opts);
|
|||
|
|
|||
|
const urlLoaderPrefix = opts.urlLoaderScheme + ":";
|
|||
|
|
|||
|
let workerFiles;
|
|||
|
let isEsmOutput = false;
|
|||
|
return {
|
|||
|
name: "off-main-thread",
|
|||
|
|
|||
|
async buildStart(options) {
|
|||
|
workerFiles = [];
|
|||
|
},
|
|||
|
|
|||
|
outputOptions({ format }) {
|
|||
|
if ((format === "esm" || format === "es") && !opts.silenceESMWorkerWarning) {
|
|||
|
this.warn(
|
|||
|
'Very few browsers support ES modules in Workers. If you want to your code to run in all browsers, set `output.format = "amd";`'
|
|||
|
);
|
|||
|
// In ESM, we never prepend a loader.
|
|||
|
isEsmOutput = true;
|
|||
|
} else if (format !== "amd") {
|
|||
|
this.error(
|
|||
|
`\`output.format\` must either be "amd" or "esm", got "${format}"`
|
|||
|
);
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
async resolveId(id, importer) {
|
|||
|
if (!id.startsWith(urlLoaderPrefix)) return;
|
|||
|
|
|||
|
const path = id.slice(urlLoaderPrefix.length);
|
|||
|
const resolved = await this.resolve(path, importer);
|
|||
|
if (!resolved) throw Error(`Cannot find module '${path}' from '${importer}'`);
|
|||
|
const newId = resolved.id;
|
|||
|
|
|||
|
return urlLoaderPrefix + newId;
|
|||
|
},
|
|||
|
|
|||
|
load(id) {
|
|||
|
if (!id.startsWith(urlLoaderPrefix)) return;
|
|||
|
|
|||
|
const realId = id.slice(urlLoaderPrefix.length);
|
|||
|
const chunkRef = this.emitFile({ id: realId, type: "chunk" });
|
|||
|
return `export default import.meta.ROLLUP_FILE_URL_${chunkRef};`;
|
|||
|
},
|
|||
|
|
|||
|
async transform(code, id) {
|
|||
|
// Copy the regexp as they are stateful and this hook is async.
|
|||
|
const workerRegexp = new RegExp(
|
|||
|
opts.workerRegexp.source,
|
|||
|
opts.workerRegexp.flags
|
|||
|
);
|
|||
|
if (!workerRegexp.test(code)) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
const ms = new MagicString(code);
|
|||
|
// Reset the regexp
|
|||
|
workerRegexp.lastIndex = 0;
|
|||
|
while (true) {
|
|||
|
const match = workerRegexp.exec(code);
|
|||
|
if (!match) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
const workerFile = match[2];
|
|||
|
let optionsObject = {};
|
|||
|
// Parse the optional options object
|
|||
|
if (match[3] && match[3].length > 0) {
|
|||
|
// FIXME: ooooof!
|
|||
|
optionsObject = new Function(`return ${match[3].slice(1)};`)();
|
|||
|
}
|
|||
|
if (!isEsmOutput) {
|
|||
|
delete optionsObject.type;
|
|||
|
}
|
|||
|
|
|||
|
if (!new RegExp("^.*/").test(workerFile)) {
|
|||
|
this.warn(
|
|||
|
`Paths passed to the Worker constructor must be relative or absolute, i.e. start with /, ./ or ../ (just like dynamic import!). Ignoring "${workerFile}".`
|
|||
|
);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
const resolvedWorkerFile = (await this.resolve(workerFile, id)).id;
|
|||
|
workerFiles.push(resolvedWorkerFile);
|
|||
|
const chunkRefId = this.emitFile({
|
|||
|
id: resolvedWorkerFile,
|
|||
|
type: "chunk"
|
|||
|
});
|
|||
|
|
|||
|
const workerParametersStartIndex = match.index + "new Worker(".length;
|
|||
|
const workerParametersEndIndex =
|
|||
|
match.index + match[0].length - ")".length;
|
|||
|
|
|||
|
ms.overwrite(
|
|||
|
workerParametersStartIndex,
|
|||
|
workerParametersEndIndex,
|
|||
|
`import.meta.ROLLUP_FILE_URL_${chunkRefId}, ${JSON.stringify(
|
|||
|
optionsObject
|
|||
|
)}`
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
return {
|
|||
|
code: ms.toString(),
|
|||
|
map: ms.generateMap({ hires: true })
|
|||
|
};
|
|||
|
},
|
|||
|
|
|||
|
resolveFileUrl(chunk) {
|
|||
|
return `"./${chunk.fileName}"`;
|
|||
|
},
|
|||
|
|
|||
|
renderChunk(code, chunk, outputOptions) {
|
|||
|
// We don’t need to do any loader processing when targeting ESM format.
|
|||
|
if (isEsmOutput) {
|
|||
|
return;
|
|||
|
}
|
|||
|
if (outputOptions.banner && outputOptions.banner.length > 0) {
|
|||
|
this.error(
|
|||
|
"OMT currently doesn’t work with `banner`. Feel free to submit a PR at https://github.com/surma/rollup-plugin-off-main-thread"
|
|||
|
);
|
|||
|
return;
|
|||
|
}
|
|||
|
const ms = new MagicString(code);
|
|||
|
|
|||
|
// Mangle define() call
|
|||
|
const id = `./${chunk.fileName}`;
|
|||
|
ms.remove(0, "define(".length);
|
|||
|
// If the module does not have any dependencies, it’s technically okay
|
|||
|
// to skip the dependency array. But our minimal loader expects it, so
|
|||
|
// we add it back in.
|
|||
|
if (!code.startsWith("define([")) {
|
|||
|
ms.prepend("[],");
|
|||
|
}
|
|||
|
ms.prepend(`${opts.amdFunctionName}("${id}",`);
|
|||
|
|
|||
|
// Prepend loader if it’s an entry point or a worker file
|
|||
|
if (opts.prependLoader(chunk, workerFiles)) {
|
|||
|
ms.prepend(opts.loader);
|
|||
|
}
|
|||
|
|
|||
|
return {
|
|||
|
code: ms.toString(),
|
|||
|
map: ms.generateMap({ hires: true })
|
|||
|
};
|
|||
|
}
|
|||
|
};
|
|||
|
};
|