-
-
Notifications
You must be signed in to change notification settings - Fork 6.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: serviceworker #7163
Comments
related: #2248 |
I would love to create a PR for that if the proposed options additions are approved by a maintainer! |
@mathe42 you can check vite plugin pwa, it supports also development mode, check docs here https://vite-plugin-pwa.netlify.app/guide/development.html |
I want full control over the SW so not what I need.... |
That isn't true, it can be modules if you use the right option. https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register |
Yes but as far as I know No Browser actualy Supports this option. |
It seems to work in Chrome for me just fine Anyways, here's a small plugin mostly copied from the existing worker plugin built into vite that we're using to do this: import {createHash} from 'crypto';
import path from 'path';
import isNil from 'lodash/isNil';
import type Rollup from 'rollup';
import {Plugin, ResolvedConfig} from 'vite';
const requestQuerySplitRE = /\?(?!.*[/|}])/;
function parseRequest(id: string): Record<string, string> | null {
const [, search] = id.split(requestQuerySplitRE, 2);
return search ? Object.fromEntries(new URLSearchParams(search)) : null;
}
const queryRE = /\?.*$/s;
const hashRE = /#.*$/s;
function cleanUrl(url: string): string {
return url.replace(hashRE, '').replace(queryRE, '');
}
function getAssetHash(content: Buffer): string {
return createHash('sha256').update(content).digest('hex').slice(0, 8);
}
function emitSourcemapForWorkerEntry(
ctx: Rollup.TransformPluginContext,
config: ResolvedConfig,
id: string,
chunk: Rollup.OutputChunk
): Buffer {
if (chunk.map) {
const basename = path.parse(cleanUrl(id)).name;
const data = chunk.map.toString();
const content = Buffer.from(data);
const contentHash = getAssetHash(content);
const fileName = `${basename}.${contentHash}.js.map`;
const filePath = path.posix.join(config.build.assetsDir, fileName);
ctx.emitFile({
fileName: filePath,
type: 'asset',
source: data,
});
chunk.code += `//# sourceMappingURL=${fileName}`;
}
return Buffer.from(chunk.code);
}
export async function bundleWorkerEntry(
ctx: Rollup.TransformPluginContext,
config: ResolvedConfig,
id: string
): Promise<Buffer> {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const rollup = require('rollup') as typeof Rollup;
const {plugins, rollupOptions, format} = config.worker;
const bundle = await rollup.rollup({
...rollupOptions,
input: cleanUrl(id),
plugins,
preserveEntrySignatures: false,
});
let chunk: Rollup.OutputChunk;
try {
const {
output: [outputChunk, ...outputChunks],
} = await bundle.generate({
format,
sourcemap: config.build.sourcemap,
});
chunk = outputChunk;
outputChunks.forEach((outputChunk) => {
if (outputChunk.type === 'asset') {
ctx.emitFile(outputChunk);
} else if (outputChunk.type === 'chunk') {
ctx.emitFile({
fileName: path.posix.join(config.build.assetsDir, outputChunk.fileName),
source: outputChunk.code,
type: 'asset',
});
}
});
} finally {
await bundle.close();
}
return emitSourcemapForWorkerEntry(ctx, config, id, chunk);
}
export async function workerFileToUrl(
ctx: Rollup.TransformPluginContext,
config: ResolvedConfig,
id: string
): Promise<string> {
const code = await bundleWorkerEntry(ctx, config, id);
const basename = path.parse(cleanUrl(id)).name;
const contentHash = getAssetHash(code);
const fileName = path.posix.join(config.build.assetsDir, `${basename}.${contentHash}.js`);
const hash = ctx.emitFile({
fileName,
type: 'asset',
source: code,
});
return `__VITE_ASSET__${hash}__`;
}
export function serviceWorkerPlugin(): Plugin {
let resolved: ResolvedConfig;
return {
name: 'serviceworker',
configResolved(config) {
resolved = config;
},
load(id) {
const parsedQuery = parseRequest(id);
if (!parsedQuery || isNil(parsedQuery.serviceworker)) {
return;
}
if (resolved.command === 'build') {
return '';
} else {
return `export default ${JSON.stringify({
url: cleanUrl(id),
options: {type: 'module'},
})};`;
}
},
async transform(_, id) {
if (resolved.command !== 'build') {
return;
}
const parsedQuery = parseRequest(id);
if (!parsedQuery || isNil(parsedQuery.serviceworker)) {
return;
}
const url = await workerFileToUrl(this, resolved, id);
return {
code: `export default ${JSON.stringify({
url: url,
options: {type: resolved.worker.format === 'es' ? 'module' : 'classic'},
})};`,
map: {mappings: ''},
};
},
};
} |
Thats great! I will have a Look into it but I currently have No Time to work on it. If there is a vite-team member that approve that this is a Feature to add to Vite I would take some time to implement it and create a PR |
Closing this in favour of #2248 |
I can confirm that modules do work in ServiceWorkers today. Tested Chrome and Safari. |
Clear and concise description of the problem
With vite 2.8 we got better worker support with a custom build pipeline.
I propose the same for serviceworker so the following should be transformed:
Suggested solution
This could be a copy of the current worker implementation with the difference that service worker can't be modules so it allways has to get compiled to an iife. This is also true while in development.
So options should be something like:
This has some open questions:
Alternative
No response
Additional context
The same might be usable for Worklets. (Audio, Paint, Animation, Layout)
Validations
The text was updated successfully, but these errors were encountered: