Skip to content
Permalink
Browse files

Hook into asset emit to append prefetching logic (#210)

* Hook into asset emit to append prefetching logic

This will work only with webpack 4.39.0 and above!

* v0.4.0

* v0.4.1

* Fix windows issues

* Improve the runtime chunk selection

* Fix next tests

* Improve next specs

* v0.4.2

* Update package deps

* v0.4.3

* Fix e2e test

* Update tsconfig.json outs

* v0.4.4
  • Loading branch information
mgechev committed Sep 13, 2019
1 parent 020df08 commit 8e347ff7a453e68732e7340edf5af7531022ee48
@@ -3,7 +3,9 @@ node_js: stable
os: linux
sudo: required

install: npm run bootstrap
install:
- npm run bootstrap
- npm i -g yarn
script:
- npm run build
- npm run test:ci
@@ -2,10 +2,10 @@ import { execSync } from 'child_process';
import { readFileSync } from 'fs';

const enterTest = 'cd packages/guess-webpack/test/fixtures/angular';
execSync(`${enterTest} && npm i`);
execSync(
console.log(execSync(`${enterTest} && yarn`).toString());
console.log(execSync(
`${enterTest} && ./node_modules/.bin/ng build --extra-webpack-config webpack.extra.js`
);
).toString());

// Prefetching instruction for baz
const fooModule = readFileSync('packages/guess-webpack/test/fixtures/angular/dist/angular/foo-foo-module.js').toString();
@@ -22,13 +22,13 @@ if (bazModule.indexOf('__GUESS__') >= 0) {
}

// No runtime
const mainModule = readFileSync('packages/guess-webpack/test/fixtures/angular/dist/angular/vendor.js').toString();
const mainModule = readFileSync('packages/guess-webpack/test/fixtures/angular/dist/angular/main.js').toString();
if (mainModule.indexOf('__GUESS__.p(') < 0 && mainModule.indexOf('__GUESS__.p=') < 0) {
console.error('Unable to find runtime or initial prefetching instruction');
process.exit(1);
}

// Prod build should work
execSync(
console.log(execSync(
`${enterTest} && ./node_modules/.bin/ng build --prod --extra-webpack-config webpack.extra.js`
);
).toString());
@@ -3,5 +3,5 @@
"packages": [
"packages/*"
],
"version": "0.3.13"
"version": "0.4.4"
}

Some generated files are not rendered by default. Learn more.

@@ -1,6 +1,6 @@
{
"name": "guess-ga",
"version": "0.3.13",
"version": "0.4.4",
"description": "Fetch structured data from Google Analytics",
"main": "dist/guess-ga/index.js",
"types": "dist/guess-ga/index.d.ts",
@@ -27,6 +27,10 @@
"dependencies": {
"googleapis": "^43.0.0"
},
"devDependencies": {
"ts-loader": "6.0.4",
"typescript": "3.6.3"
},
"files": [
"dist"
],
@@ -44,10 +48,6 @@
"node"
]
},
"devDependencies": {
"ts-loader": "6.0.4",
"typescript": "3.6.3"
},
"scripts": {
"build": "webpack"
}
@@ -6,7 +6,7 @@
"declaration": true,
"experimentalDecorators": true,
"strict": true,
"outDir": ".",
"outDir": "dist",
"downlevelIteration": true,
"lib": ["es2015", "esnext", "dom"]
},

Some generated files are not rendered by default. Learn more.

@@ -1,6 +1,6 @@
{
"name": "guess-parser",
"version": "0.3.13",
"version": "0.4.4",
"description": "Finds the route declarations in your application.",
"main": "dist/guess-parser/index.js",
"types": "dist/guess-parser/index.d.ts",
@@ -25,6 +25,9 @@
"dependencies": {
"@wessberg/ts-evaluator": "0.0.22"
},
"devDependencies": {
"ts-loader": "6.0.4"
},
"peerDependencies": {
"typescript": "3.6.3"
},
@@ -45,9 +48,6 @@
"node"
]
},
"devDependencies": {
"ts-loader": "6.0.4"
},
"scripts": {
"build": "webpack"
}
@@ -235,7 +235,7 @@ const getRoute = (
const loadChildren = readLoadChildren(node, program.getTypeChecker());
if (loadChildren) {
const parent = getModuleEntryPoint(
node.getSourceFile().fileName,
resolve(node.getSourceFile().fileName),
entryPoints,
program
);
@@ -364,7 +364,7 @@ const findMainModule = (program: ts.Program) => {
if (!decl) {
return null;
}
return decl[0].getSourceFile().fileName;
return resolve(decl[0].getSourceFile().fileName);
}
let mainPath: null | string = null;
n.forEachChild(c => {
@@ -399,7 +399,7 @@ const getLazyEntryPoints = (
return null;
}

const parent = node.getSourceFile().fileName;
const parent = resolve(node.getSourceFile().fileName);
const module = getModulePathFromRoute(parent, value);
return module;
};
@@ -438,7 +438,7 @@ export const parseRoutes = (
callback: (routeObj: ts.Node) => void,
n: ts.Node
) => {
if (excludeFiles.has(s.fileName)) {
if (excludeFiles.has(resolve(s.fileName))) {
return;
}
if (!n) {
@@ -476,7 +476,7 @@ export const parseRoutes = (
program.getSourceFiles().map(s => {
s.forEachChild(
visitNode.bind(null, s, (n: ts.Node) => {
const path = n.getSourceFile().fileName;
const path = resolve(n.getSourceFile().fileName);
const route = getRoute(
n as ts.ObjectLiteralExpression,
entryPoints,
@@ -6,7 +6,7 @@
"declaration": true,
"experimentalDecorators": true,
"strict": true,
"outDir": ".",
"outDir": "dist",
"downlevelIteration": true,
"lib": ["es2015", "esnext", "dom"]
},

Some generated files are not rendered by default. Learn more.

@@ -1,6 +1,6 @@
{
"name": "guess-webpack",
"version": "0.3.13",
"version": "0.4.4",
"description": "Webpack plugins for the Machine Learning-driven bundler",
"main": "dist/guess-webpack/main.js",
"types": "dist/guess-webpack/index.d.ts",
@@ -28,7 +28,7 @@
"flat-cache": "^2.0.0",
"google-oauth2-node": "0.0.3",
"googleapis": "^43.0.0",
"guess-ga": "^0.3.13",
"guess-ga": "^0.4.4",
"lodash.template": "^4.4.0",
"rollup": "^1.0.0",
"rollup-plugin-hypothetical": "^2.1.0",
@@ -0,0 +1,19 @@
export interface Asset {
name: string;
callback: () => void;
}

export type AssetCallback = (asset: Asset) => void;

export class AssetObserver {
buffer: Asset[] = [];
private _callbacks: AssetCallback[] = [];

onAsset(cb: AssetCallback) {
this._callbacks.push(cb);
}

addAsset(asset: Asset) {
this._callbacks.forEach(cb => cb(asset));
}
}
@@ -1,12 +1,9 @@
import { RouteProvider, PrefetchConfig } from './declarations';
import { PrefetchPlugin } from './prefetch-plugin';
import { PrefetchAotPlugin } from './prefetch-aot-plugin';
import {
Graph,
RoutingModule,
Period,
} from '../../common/interfaces';
import { Graph, RoutingModule, Period } from '../../common/interfaces';
import { getReport } from './ga-provider';
import { AssetObserver } from './asset-observer';

export interface RuntimeConfig {
/** @internal */
@@ -60,19 +57,39 @@ export class GuessPlugin {
}

apply(compiler: any) {
compiler.hooks.emit.tapAsync({
stage: 0,
name: 'GuessPlugin'
}, (compilation: any, cb: any) =>
this._execute(compilation, cb)
const assetObserver = new AssetObserver();
if (!this._config.runtime || !this._config.runtime.delegate) {
compiler.hooks.assetEmitted.tapAsync(
'GuessPlugin',
(file: string, _: any, callback: any) =>
assetObserver.addAsset({
name: file,
callback
})
);
}

compiler.hooks.emit.tapAsync(
{
stage: 0,
name: 'GuessPlugin'
},
(compilation: any, cb: any) => this._execute(compiler, compilation, assetObserver, cb)
);
}

private _execute(compilation: any, cb: any) {
private _execute(compiler: any, compilation: any, assetObserver: AssetObserver, cb: any) {
extractRoutes(this._config).then(async routes => {
try {
const data = await this._getReport(routes);
return this._executePrefetchPlugin(data, routes, compilation, cb);
return this._executePrefetchPlugin(
data,
routes,
compiler,
compilation,
assetObserver,
cb
);
} catch (err) {
console.error(err);
cb();
@@ -98,7 +115,9 @@ export class GuessPlugin {
private _executePrefetchPlugin(
data: Graph,
routes: RoutingModule[],
compiler: any,
compilation: any,
assetObserver: AssetObserver,
cb: any
) {
const { runtime } = this._config;
@@ -125,8 +144,8 @@ export class GuessPlugin {
: runtime.basePath
: '',
prefetchConfig: runtime ? runtime.prefetchConfig : undefined,
routes,
}).execute(compilation, cb);
routes
}).execute(compiler, compilation, assetObserver, cb);
}
}
}

0 comments on commit 8e347ff

Please sign in to comment.
You can’t perform that action at this time.