Skip to content

Commit

Permalink
[Next.js] Exclude ComponentProps functions from the client bundle (#1753
Browse files Browse the repository at this point in the history
)

* [Next.js] Exclude ComponentProps functions from the client bundle

* Updated CHANGELOG

* Update

* Updated yarn.lock

* Added a submodule

* Extracted common function
  • Loading branch information
illiakovalenko authored Mar 6, 2024
1 parent 44d995d commit 74e2e36
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Our versioning strategy is as follows:

### 🐛 Bug Fixes

* `[templates/nextjs]` Exclude ComponentProps functions from the client bundle ([#1753](https://github.com/Sitecore/jss/pull/1753))
* `[sitecore-jss]` Any unused personalized component variants are deleted before sending layout data to the client, thus are completely hidden from the customer. ([#1752](https://github.com/Sitecore/jss/pull/1752))

## 21.6.3
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* @param {import('next').NextConfig} nextConfig
*/
const componentPropsPlugin = (nextConfig = {}) => {
return Object.assign({}, nextConfig, {
webpack: (config, options) => {

if (!options.isServer) {
// Add a loader to strip out getServerSideProps and getStaticProps from components in the client bundle
config.module.rules.unshift({
test: /src\\components\\.*\.tsx$/,
use: ['@sitecore-jss\\sitecore-jss-dev-tools\\nextjs-component-props-loader'],
});
}

// Overload the Webpack config if it was already overloaded
if (typeof nextConfig.webpack === 'function') {
return nextConfig.webpack(config, options);
}

return config;
},
});
};

module.exports = componentPropsPlugin;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './types/templating/nextjs/component-props.loader';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const loader = require('./dist/cjs/templating/nextjs/component-props.loader');

module.exports = loader;
2 changes: 2 additions & 0 deletions packages/sitecore-jss-dev-tools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"url": "https://github.com/sitecore/jss/issues"
},
"dependencies": {
"@babel/parser": "^7.24.0",
"@sitecore-jss/sitecore-jss": "21.7.0-canary.77",
"axios": "^1.3.2",
"chalk": "^4.1.2",
Expand All @@ -47,6 +48,7 @@
"jszip": "^3.10.1",
"module-alias": "^2.2.2",
"readline-sync": "^1.4.10",
"recast": "^0.23.5",
"resolve": "^1.22.1",
"ts-node": "^10.9.1",
"url-join": "^4.0.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import loader from './component-props.loader';
import { expect } from 'chai';

describe('component-props.loader', () => {
it('should strip exported function expressions from the source code', () => {
const source = `import { foo } from 'module';
console.log('Hello, world!');
export const getServerSideProps = async () => {
return {
props: { test: true },
};
}
export const getStaticProps = async () => {
return {
props: { test: true },
};
}`;

const expected = `import { foo } from 'module';
console.log('Hello, world!');`;

const result = loader(source).replace(/\r\n/g, '\n');

expect(result).to.deep.equal(expected);
});

it('should strip exported function declarations from the source code', () => {
const source = `import { foo } from 'module';
console.log('Hello, world!');
export async function getServerSideProps() {
return {
props: { test: true },
};
}
export async function getStaticProps() {
return {
props: { test: true },
};
}`;

const expected = `import { foo } from 'module';
console.log('Hello, world!');
export {};
export {};`;

const result = loader(source).replace(/\r\n/g, '\n');

expect(result).to.deep.equal(expected);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import * as recast from 'recast';

type VariableDeclaration = recast.types.namedTypes.VariableDeclaration;

/**
* Webpack loader to strip functions from the source code
* Strips the `getServerSideProps` and `getStaticProps` functions from the source code
* @param {string} source file source code
* @returns {string} output file source code with stripped functions
*/
export default function componentPropsLoader(source: string) {
// Parse the source code into an AST (Abstract Syntax Tree)
const ast = recast.parse(source, {
parser: require('recast/parsers/babel-ts'),
});

// List of functions to strip from the AST
const functionsToStrip = ['getServerSideProps', 'getStaticProps'];

// Remove the function from the list of functions to strip
const updateList = (functionName: string) => {
// Remove the function from the list of functions to strip
functionsToStrip.splice(functionsToStrip.indexOf(functionName), 1);
};

// Traverse the AST and strip the functions
recast.visit(ast, {
// Visit the named export function expression
visitExportNamedDeclaration: function(path): boolean | void {
// Get the variable declaration from the AST
(path.node.declaration as VariableDeclaration)?.declarations?.forEach((declaration) => {
// Check if the function is in the list of functions to strip
if (
'id' in declaration &&
'name' in declaration.id &&
typeof declaration.id.name === 'string' &&
functionsToStrip.includes(declaration.id.name)
) {
updateList(declaration.id.name);

// Strip the function from the AST
path.prune();
}
});

if (functionsToStrip.length === 0) {
// We have pruned all the functions we need to, so we can stop traversing the AST
return false;
}

// Continue traversing the AST
this.traverse(path);
},
// Visit the named export function declaration
visitFunctionDeclaration: function(path): boolean | void {
// Check if the function is in the list of functions to strip
if (
path.node.id &&
'name' in path.node.id &&
typeof path.node.id.name === 'string' &&
functionsToStrip.includes(path.node.id.name)
) {
updateList(path.node.id.name);

// Strip the function from the AST
path.prune();
}

if (functionsToStrip.length === 0) {
// We have pruned all the functions we need to, so we can stop traversing the AST
return false;
}

// Continue traversing the AST
this.traverse(path);
},
});

// Generate the output code
return recast.print(ast).code;
}
3 changes: 2 additions & 1 deletion packages/sitecore-jss-dev-tools/tsconfig-esm.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"node_modules",
"src/**/*.test.ts",
"react.d.ts",
"nextjs.d.ts"
"nextjs.d.ts",
"nextjs-component-props-loader.d.ts",
]
}
3 changes: 2 additions & 1 deletion packages/sitecore-jss-dev-tools/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"src/test",
"src/**/*.test.ts",
"react.d.ts",
"nextjs.d.ts"
"nextjs.d.ts",
"nextjs-component-props-loader.d.ts",
]
}
44 changes: 42 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,15 @@ __metadata:
languageName: node
linkType: hard

"@babel/parser@npm:^7.24.0":
version: 7.24.0
resolution: "@babel/parser@npm:7.24.0"
bin:
parser: ./bin/babel-parser.js
checksum: 4a6afec49487a212e7a27345b0c090b56905efb62c0b3a1499b0a57a5b3bf43d9d1e99e31b137080eacc24dee659a29699740d0a6289999117c0d8c5a04bd68f
languageName: node
linkType: hard

"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.22.5, @babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.23.3":
version: 7.23.3
resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.23.3"
Expand Down Expand Up @@ -5425,6 +5434,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@sitecore-jss/sitecore-jss-dev-tools@workspace:packages/sitecore-jss-dev-tools"
dependencies:
"@babel/parser": ^7.24.0
"@sitecore-jss/sitecore-jss": 21.7.0-canary.77
"@types/chai": ^4.3.4
"@types/chokidar": ^2.1.3
Expand Down Expand Up @@ -5463,6 +5473,7 @@ __metadata:
nock: ^13.3.0
nyc: ^15.1.0
readline-sync: ^1.4.10
recast: ^0.23.5
resolve: ^1.22.1
sinon: ^15.0.1
ts-node: ^10.9.1
Expand Down Expand Up @@ -8505,6 +8516,15 @@ __metadata:
languageName: node
linkType: hard

"ast-types@npm:^0.16.1":
version: 0.16.1
resolution: "ast-types@npm:0.16.1"
dependencies:
tslib: ^2.0.1
checksum: 21c186da9fdb1d8087b1b7dabbc4059f91aa5a1e593a9776b4393cc1eaa857e741b2dda678d20e34b16727b78fef3ab59cf8f0c75ed1ba649c78fe194e5c114b
languageName: node
linkType: hard

"astral-regex@npm:^1.0.0":
version: 1.0.0
resolution: "astral-regex@npm:1.0.0"
Expand Down Expand Up @@ -13226,7 +13246,7 @@ __metadata:
languageName: node
linkType: hard

"esprima@npm:^4.0.0, esprima@npm:^4.0.1":
"esprima@npm:^4.0.0, esprima@npm:^4.0.1, esprima@npm:~4.0.0":
version: 4.0.1
resolution: "esprima@npm:4.0.1"
bin:
Expand Down Expand Up @@ -22464,6 +22484,19 @@ __metadata:
languageName: node
linkType: hard

"recast@npm:^0.23.5":
version: 0.23.5
resolution: "recast@npm:0.23.5"
dependencies:
ast-types: ^0.16.1
esprima: ~4.0.0
source-map: ~0.6.1
tiny-invariant: ^1.3.3
tslib: ^2.0.1
checksum: 1b9d71f3166ab4a116c1b551059f1c9ae22f38568c0393635e31642f05b52bdec6cdab98a6fc3fe4bb995055a620bcbbf931a1a82c362e39669b77b55252649a
languageName: node
linkType: hard

"redent@npm:^3.0.0":
version: 3.0.0
resolution: "redent@npm:3.0.0"
Expand Down Expand Up @@ -24806,6 +24839,13 @@ __metadata:
languageName: node
linkType: hard

"tiny-invariant@npm:^1.3.3":
version: 1.3.3
resolution: "tiny-invariant@npm:1.3.3"
checksum: 5e185c8cc2266967984ce3b352a4e57cb89dad5a8abb0dea21468a6ecaa67cd5bb47a3b7a85d08041008644af4f667fb8b6575ba38ba5fb00b3b5068306e59fe
languageName: node
linkType: hard

"tmp@npm:^0.0.33":
version: 0.0.33
resolution: "tmp@npm:0.0.33"
Expand Down Expand Up @@ -25145,7 +25185,7 @@ __metadata:
languageName: node
linkType: hard

"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0":
"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0":
version: 2.6.2
resolution: "tslib@npm:2.6.2"
checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad
Expand Down

0 comments on commit 74e2e36

Please sign in to comment.