Skip to content
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

Add support for injecting manifest into service workers #6798

Merged
merged 10 commits into from
Sep 1, 2021
3 changes: 2 additions & 1 deletion packages/configs/default/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
"runtimes": [
"@parcel/runtime-js",
"@parcel/runtime-browser-hmr",
"@parcel/runtime-react-refresh"
"@parcel/runtime-react-refresh",
"@parcel/runtime-service-worker"
],
"optimizers": {
"data-url:*": ["...", "@parcel/optimizer-data-url"],
Expand Down
1 change: 1 addition & 0 deletions packages/configs/default/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@parcel/runtime-browser-hmr": "2.0.0-rc.0",
"@parcel/runtime-js": "2.0.0-rc.0",
"@parcel/runtime-react-refresh": "2.0.0-rc.0",
"@parcel/runtime-service-worker": "2.0.0-rc.0",
"@parcel/transformer-babel": "2.0.0-rc.0",
"@parcel/transformer-css": "2.0.0-rc.0",
"@parcel/transformer-html": "2.0.0-rc.0",
Expand Down
9 changes: 5 additions & 4 deletions packages/core/core/src/BundleGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {ContentGraph, ALL_EDGE_TYPES, mapVisitor} from '@parcel/graph';
import {Hash, hashString} from '@parcel/hash';
import {objectSortedEntriesDeep, getRootDir} from '@parcel/utils';

import {Priority, BundleBehavior} from './types';
import {Priority, BundleBehavior, SpecifierType} from './types';
import {getBundleGroupId, getPublicId} from './utils';
import {ISOLATED_ENVS} from './public/Environment';
import {fromProjectPath} from './projectPath';
Expand Down Expand Up @@ -703,11 +703,12 @@ export default class BundleGraph {
this._graph
.getNodeIdsConnectedTo(assetNodeId, 'references')
.map(id => this._graph.getNode(id))
.filter(
.some(
node =>
node?.type === 'dependency' &&
node.value.priority === Priority.lazy,
).length > 0
node.value.priority === Priority.lazy &&
node.value.specifierType !== SpecifierType.url,
)
) {
// If this asset is referenced by any async dependency, it's referenced.
return true;
Expand Down
4 changes: 3 additions & 1 deletion packages/core/core/src/requests/EntryRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,9 @@ export class EntryResolver {
if (!allTargetsHaveSource && pkg.source != null) {
let pkgSources = Array.isArray(pkg.source)
? pkg.source
: [pkg.source];
: typeof pkg.source === 'string'
? [pkg.source]
: [];
devongovett marked this conversation as resolved.
Show resolved Hide resolved
let i = 0;
for (let pkgSource of pkgSources) {
let source = path.join(path.dirname(filePath), pkgSource);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import {manifest, version} from '@parcel/runtime-service-worker';

output(manifest, version);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
navigator.serviceWorker.register(new URL('manifest-worker.js', import.meta.url), {type: 'module'});
33 changes: 33 additions & 0 deletions packages/core/integration-tests/test/javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -1587,6 +1587,39 @@ describe('javascript', function() {
assert(errored);
});

it('should expose a manifest to service workers', async function() {
let b = await bundle(
path.join(__dirname, '/integration/service-worker/manifest.js'),
{
defaultTargetOptions: {
shouldScopeHoist: true,
},
},
);

assertBundles(b, [
{
name: 'manifest.js',
assets: ['manifest.js', 'bundle-url.js'],
},
{
assets: ['manifest-worker.js', 'browser.js'],
},
]);

let bundles = b.getBundles();
let worker = bundles.find(b => b.env.isWorker());
let manifest, version;
await await runBundle(b, worker, {
output(m, v) {
manifest = m;
version = v;
},
});
assert.deepEqual(manifest, ['/manifest.js']);
assert.equal(typeof version, 'string');
});

it('should recognize serviceWorker.register with static URL and import.meta.url', async function() {
let b = await bundle(
path.join(
Expand Down
10 changes: 9 additions & 1 deletion packages/core/package-manager/src/NodeResolverBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,15 @@ export class NodeResolverBase<T> {
pkg.name.startsWith('@parcel/') &&
pkg.name !== '@parcel/watcher'
) {
main = pkg.source;
if (
pkg.main &&
typeof pkg.source === 'object' &&
!Array.isArray(pkg.source)
) {
main = pkg.source[pkg.main];
} else {
main = pkg.source;
}
}

return [main]
Expand Down
3 changes: 2 additions & 1 deletion packages/core/test-utils/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,8 @@ export async function runBundles(
promises = prepared.promises;
break;
}
case 'web-worker': {
case 'web-worker':
case 'service-worker': {
let prepared = prepareWorkerContext(parent.filePath, globals);
ctx = prepared.ctx;
promises = prepared.promises;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/types/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ export type PackageJSON = {
module?: FilePath,
types?: FilePath,
browser?: FilePath | {[FilePath]: FilePath | boolean, ...},
source?: FilePath | Array<FilePath>,
source?: FilePath | Array<FilePath> | {|[string]: string|},
alias?: {[PackageName | FilePath | Glob]: PackageName | FilePath, ...},
browserslist?: Array<string> | {[string]: Array<string>},
engines?: Engines,
Expand Down
2 changes: 1 addition & 1 deletion packages/packagers/js/src/ScopeHoistingPackager.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ export class ScopeHoistingPackager {
this.bundleGraph.isAssetReferenced(this.bundle, asset) ||
this.bundleGraph
.getIncomingDependencies(asset)
.some(dep => dep.meta.shouldWrap)
.some(dep => dep.meta.shouldWrap && dep.specifierType !== 'url')
) {
this.wrappedAssets.add(asset.id);
return true;
Expand Down
2 changes: 2 additions & 0 deletions packages/runtimes/service-worker/browser.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const manifest: string[];
export const version: string;
8 changes: 8 additions & 0 deletions packages/runtimes/service-worker/browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export let manifest = [];
export let version = '';

// Called by the runtime.
export function _register(m, v) {
manifest = m;
version = v;
}
32 changes: 32 additions & 0 deletions packages/runtimes/service-worker/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "@parcel/runtime-service-worker",
"version": "2.0.0-rc.0",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"repository": {
"type": "git",
"url": "https://github.com/parcel-bundler/parcel.git"
},
"main": "./lib/ServiceWorkerRuntime.js",
"source": {
"./lib/ServiceWorkerRuntime.js": "./src/ServiceWorkerRuntime.js"
},
"browser": "browser.js",
mischnic marked this conversation as resolved.
Show resolved Hide resolved
"types": "browser.d.ts",
"sideEffects": false,
"engines": {
"node": ">= 12.0.0",
"parcel": "^2.0.0-beta.1"
},
"dependencies": {
"@parcel/plugin": "2.0.0-rc.0",
"@parcel/utils": "2.0.0-rc.0",
"nullthrows": "^1.1.1"
}
}
50 changes: 50 additions & 0 deletions packages/runtimes/service-worker/src/ServiceWorkerRuntime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// @flow
import {Runtime} from '@parcel/plugin';
import {urlJoin} from '@parcel/utils';

export default (new Runtime({
async apply({bundle, bundleGraph}) {
if (bundle.env.context !== 'service-worker') {
return [];
}

let hasDep;
bundle.traverse((node, _, actions) => {
if (
node.type === 'dependency' &&
node.value.specifier === '@parcel/runtime-service-worker'
) {
hasDep = true;
actions.stop();
}
});

if (!hasDep) {
return [];
}

let manifest = [];
bundleGraph.traverseBundles((b, _, actions) => {
if (b.bundleBehavior === 'inline' || b.id === bundle.id) {
return;
}

manifest.push(urlJoin(b.target.publicUrl, b.name));
});

let code = `import {_register} from '@parcel/runtime-service-worker';
const manifest = ${JSON.stringify(manifest)};
const version = ${JSON.stringify(bundle.hashReference)};
_register(manifest, version);
`;

return [
{
filePath: __filename,
code,
isEntry: true,
env: {sourceType: 'module'},
},
];
},
}): Runtime);