-
Notifications
You must be signed in to change notification settings - Fork 72
implement webpack chunks file updating using ast manipulation #12
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
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
e3c865c
implement webpack chunks file updating using ast manipulation
dario-piotrowicz 1b0f146
remove no longer required `serverMinification: false` options
dario-piotrowicz 8c2e64b
update TODO and builder/README files
dario-piotrowicz 7ffd614
Apply suggestions from code review
dario-piotrowicz 5272a2a
cleanup builder dependencies and use pnpm catalog for them
dario-piotrowicz ab94bf1
remove webpackRuntimeFile log
dario-piotrowicz b1e1edd
remove `standalone` output config from example apps
dario-piotrowicz 55d780d
fixup! implement webpack chunks file updating using ast manipulation
dario-piotrowicz f000041
fixup! implement webpack chunks file updating using ast manipulation
dario-piotrowicz 5b446a5
fixup! remove `standalone` output config from example apps
dario-piotrowicz 877c0d6
fixup! implement webpack chunks file updating using ast manipulation
dario-piotrowicz 0b8ea93
fixup! implement webpack chunks file updating using ast manipulation
dario-piotrowicz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,5 @@ | |
.wrangler | ||
pnpm-lock.yaml | ||
.vscode/setting.json | ||
test-fixtures | ||
test-snapshots |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
...atches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { readFile } from "node:fs/promises"; | ||
|
||
import { expect, test, describe } from "vitest"; | ||
|
||
import { getChunkInstallationIdentifiers } from "./get-chunk-installation-identifiers"; | ||
import { tsParseFile } from "../../../utils"; | ||
|
||
describe("getChunkInstallationIdentifiers", () => { | ||
test("gets chunk identifiers from unminified code", async () => { | ||
const fileContent = await readFile( | ||
`${import.meta.dirname}/test-fixtures/unminified-webpacks-file.js`, | ||
"utf8" | ||
); | ||
const tsSourceFile = tsParseFile(fileContent); | ||
const { installChunk, installedChunks } = await getChunkInstallationIdentifiers(tsSourceFile); | ||
expect(installChunk).toEqual("installChunk"); | ||
expect(installedChunks).toEqual("installedChunks"); | ||
}); | ||
|
||
test("gets chunk identifiers from minified code", async () => { | ||
const fileContent = await readFile( | ||
`${import.meta.dirname}/test-fixtures/minified-webpacks-file.js`, | ||
"utf8" | ||
); | ||
const tsSourceFile = tsParseFile(fileContent); | ||
const { installChunk, installedChunks } = await getChunkInstallationIdentifiers(tsSourceFile); | ||
expect(installChunk).toEqual("r"); | ||
expect(installedChunks).toEqual("e"); | ||
}); | ||
}); |
102 changes: 102 additions & 0 deletions
102
...ild/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import * as ts from "ts-morph"; | ||
|
||
/** | ||
* Gets the names of the variables that in the unminified webpack runtime file are called `installedChunks` and `installChunk`. | ||
* | ||
* Variables example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L256-L282 | ||
* | ||
* @param sourceFile the webpack runtime file parsed with ts-morph | ||
* @returns an object containing the two variable names | ||
*/ | ||
export async function getChunkInstallationIdentifiers(sourceFile: ts.SourceFile): Promise<{ | ||
installedChunks: string; | ||
installChunk: string; | ||
}> { | ||
const installChunkDeclaration = getInstallChunkDeclaration(sourceFile); | ||
dario-piotrowicz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const installedChunksDeclaration = getInstalledChunksDeclaration(sourceFile, installChunkDeclaration); | ||
|
||
return { | ||
installChunk: installChunkDeclaration.getName(), | ||
installedChunks: installedChunksDeclaration.getName(), | ||
}; | ||
} | ||
|
||
/** | ||
* Gets the declaration for what in the unminified webpack runtime file is called `installChunk`(which is a function that registers the various chunks. | ||
* | ||
* `installChunk` example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L263-L282 | ||
* | ||
* @param sourceFile the webpack runtime file parsed with ts-morph | ||
* @returns the `installChunk` declaration | ||
*/ | ||
function getInstallChunkDeclaration(sourceFile: ts.SourceFile): ts.VariableDeclaration { | ||
const installChunkDeclaration = sourceFile | ||
.getDescendantsOfKind(ts.SyntaxKind.VariableDeclaration) | ||
.find((declaration) => { | ||
const arrowFunction = declaration.getInitializerIfKind(ts.SyntaxKind.ArrowFunction); | ||
// we're looking for an arrow function | ||
if (!arrowFunction) return false; | ||
|
||
const functionParameters = arrowFunction.getParameters(); | ||
// the arrow function we're looking for has a single parameter (the chunkId) | ||
if (functionParameters.length !== 1) return false; | ||
|
||
const arrowFunctionBodyBlock = arrowFunction.getFirstChildByKind(ts.SyntaxKind.Block); | ||
|
||
// the arrow function we're looking for has a block body | ||
if (!arrowFunctionBodyBlock) return false; | ||
|
||
const statementKinds = arrowFunctionBodyBlock.getStatements().map((statement) => statement.getKind()); | ||
|
||
// the function we're looking for has 2 for loops (a standard one and a for-in one) | ||
const forInStatements = statementKinds.filter((s) => s === ts.SyntaxKind.ForInStatement); | ||
const forStatements = statementKinds.filter((s) => s === ts.SyntaxKind.ForStatement); | ||
if (forInStatements.length !== 1 || forStatements.length !== 1) return false; | ||
|
||
// the function we're looking for accesses its parameter three times, and it | ||
// accesses its `modules`, `ids` and `runtime` properties (in this order) | ||
const parameterName = functionParameters[0].getText(); | ||
const functionParameterAccessedProperties = arrowFunctionBodyBlock | ||
.getDescendantsOfKind(ts.SyntaxKind.PropertyAccessExpression) | ||
.filter( | ||
(propertyAccessExpression) => propertyAccessExpression.getExpression().getText() === parameterName | ||
) | ||
.map((propertyAccessExpression) => propertyAccessExpression.getName()); | ||
if (functionParameterAccessedProperties.join(", ") !== "modules, ids, runtime") return false; | ||
|
||
return true; | ||
}); | ||
|
||
if (!installChunkDeclaration) { | ||
throw new Error("ERROR: unable to find the installChunk function declaration"); | ||
} | ||
|
||
return installChunkDeclaration; | ||
} | ||
|
||
/** | ||
* Gets the declaration for what in the unminified webpack runtime file is called `installedChunks` which is an object that holds the various registered chunks. | ||
* | ||
* `installedChunks` example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L256-L261 | ||
* | ||
* @param sourceFile the webpack runtime file parsed with ts-morph | ||
* @param installChunkDeclaration the declaration for the `installChunk` variable | ||
* @returns the `installedChunks` declaration | ||
*/ | ||
function getInstalledChunksDeclaration( | ||
dario-piotrowicz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
sourceFile: ts.SourceFile, | ||
installChunkDeclaration: ts.VariableDeclaration | ||
): ts.VariableDeclaration { | ||
const allVariableDeclarations = sourceFile.getDescendantsOfKind(ts.SyntaxKind.VariableDeclaration); | ||
const installChunkDeclarationIdx = allVariableDeclarations.findIndex( | ||
(declaration) => declaration === installChunkDeclaration | ||
); | ||
|
||
// the installedChunks declaration comes right before the installChunk one | ||
const installedChunksDeclaration = allVariableDeclarations[installChunkDeclarationIdx - 1]; | ||
|
||
if (!installedChunksDeclaration?.getInitializer()?.isKind(ts.SyntaxKind.ObjectLiteralExpression)) { | ||
throw new Error("ERROR: unable to find the installedChunks declaration"); | ||
} | ||
return installedChunksDeclaration; | ||
} |
44 changes: 44 additions & 0 deletions
44
...d/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { readFile } from "node:fs/promises"; | ||
|
||
import { expect, test, describe } from "vitest"; | ||
|
||
import { getFileContentWithUpdatedWebpackFRequireCode } from "./get-file-content-with-updated-webpack-f-require-code"; | ||
import { tsParseFile } from "../../../utils"; | ||
|
||
describe("getFileContentWithUpdatedWebpackFRequireCode", () => { | ||
test("returns the updated content of the f.require function from unminified webpack runtime code", async () => { | ||
const fileContent = await readFile( | ||
`${import.meta.dirname}/test-fixtures/unminified-webpacks-file.js`, | ||
"utf8" | ||
); | ||
const tsSourceFile = tsParseFile(fileContent); | ||
const updatedFCode = await getFileContentWithUpdatedWebpackFRequireCode( | ||
tsSourceFile, | ||
{ installChunk: "installChunk", installedChunks: "installedChunks" }, | ||
["658"] | ||
); | ||
expect(unstyleCode(updatedFCode)).toContain(`if (installedChunks[chunkId]) return;`); | ||
expect(unstyleCode(updatedFCode)).toContain( | ||
`if (chunkId === 658) return installChunk(require("./chunks/658.js"));` | ||
); | ||
}); | ||
|
||
test("returns the updated content of the f.require function from minified webpack runtime code", async () => { | ||
const fileContent = await readFile( | ||
`${import.meta.dirname}/test-fixtures/minified-webpacks-file.js`, | ||
"utf8" | ||
); | ||
const tsSourceFile = tsParseFile(fileContent); | ||
const updatedFCode = await getFileContentWithUpdatedWebpackFRequireCode( | ||
tsSourceFile, | ||
{ installChunk: "r", installedChunks: "e" }, | ||
["658"] | ||
); | ||
expect(unstyleCode(updatedFCode)).toContain("if (e[o]) return;"); | ||
expect(unstyleCode(updatedFCode)).toContain(`if (o === 658) return r(require("./chunks/658.js"));`); | ||
}); | ||
}); | ||
|
||
function unstyleCode(text: string): string { | ||
return text.replace(/\n\s+/g, "\n").replace(/\n/g, " "); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.