@navita/next-plugin 0.0.0-main-20230917201540

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ import { LoaderContext } from 'webpack';
2
+
3
+ declare function pitch(this: LoaderContext<unknown>): string;
4
+
5
+ export { pitch };
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ var webpackPlugin = require('@navita/webpack-plugin');
4
+
5
+ function pitch() {
6
+ this._module.loaders = [];
7
+ const dependency = webpackPlugin.getNavitaDependency(this._compiler.webpack);
8
+ const { cssHash , issuerPath } = Object.fromEntries(new URLSearchParams(this.resourceQuery).entries());
9
+ this._module.addDependency(new dependency(issuerPath, cssHash));
10
+ return '';
11
+ }
12
+
13
+ exports.pitch = pitch;
@@ -0,0 +1,11 @@
1
+ import { getNavitaDependency } from '@navita/webpack-plugin';
2
+
3
+ function pitch() {
4
+ this._module.loaders = [];
5
+ const dependency = getNavitaDependency(this._compiler.webpack);
6
+ const { cssHash , issuerPath } = Object.fromEntries(new URLSearchParams(this.resourceQuery).entries());
7
+ this._module.addDependency(new dependency(issuerPath, cssHash));
8
+ return '';
9
+ }
10
+
11
+ export { pitch };
package/index.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { Options } from '@navita/webpack-plugin';
2
+ import { NextConfig } from 'next';
3
+
4
+ type WebpackOptions = Options;
5
+ interface Config extends WebpackOptions {
6
+ singleCssFile?: boolean;
7
+ }
8
+ declare const createNavitaStylePlugin: (navitaConfig?: Config) => (nextConfig: NextConfig) => NextConfig;
9
+
10
+ export { createNavitaStylePlugin };
package/index.js ADDED
@@ -0,0 +1,174 @@
1
+ 'use strict';
2
+
3
+ var webpackPlugin = require('@navita/webpack-plugin');
4
+ var NextMiniCssExtractPlugin = require('next/dist/build/webpack/plugins/mini-css-extract-plugin');
5
+ var findPagesDir = require('next/dist/lib/find-pages-dir');
6
+
7
+ const merge = (...merge)=>{
8
+ const result = {};
9
+ for (const usedIds of merge){
10
+ for(const key in usedIds){
11
+ result[key] = [
12
+ ...result[key] || [],
13
+ ...usedIds[key] || []
14
+ ];
15
+ }
16
+ }
17
+ for(const key1 in result){
18
+ result[key1] = [
19
+ ...new Set(result[key1])
20
+ ];
21
+ }
22
+ return result;
23
+ };
24
+ const intersect = (...intersect)=>{
25
+ const result = {};
26
+ for (const usedIds of intersect){
27
+ for(const key in usedIds){
28
+ result[key] = result[key] ? result[key].filter((id)=>usedIds[key].includes(id)) : usedIds[key];
29
+ }
30
+ }
31
+ return result;
32
+ };
33
+ // structuredClone would be better, but since Next.js version 13 is at node 16.14.0,
34
+ // we can't use it.
35
+ const copy = (source)=>JSON.parse(JSON.stringify(source));
36
+ const removeParentFromCurrent = (usedIds, parentUsedIds)=>{
37
+ for(const key in parentUsedIds){
38
+ if (usedIds[key] && parentUsedIds[key]) {
39
+ usedIds[key] = usedIds[key].filter((id)=>!parentUsedIds[key].includes(id));
40
+ }
41
+ }
42
+ };
43
+ function optimizeCSSOutput(output) {
44
+ const nameToChunk = Object.fromEntries(Array.from(output.keys()).filter((x)=>x.name).map((x)=>[
45
+ x.name,
46
+ x
47
+ ]));
48
+ const getAllParentUsedIds = (chunk)=>{
49
+ const { name: route } = chunk;
50
+ if (!route) {
51
+ // If we don't have a name, it's a dynamic chunk.
52
+ const value = output.get(chunk);
53
+ if (value.parents.length === 0) {
54
+ return [];
55
+ }
56
+ return [
57
+ intersect(...value.parents.map((parent)=>merge(...getAllParentUsedIds(parent), output.get(parent).usedIds)))
58
+ ];
59
+ }
60
+ if (route.startsWith('pages/')) {
61
+ const routes = [
62
+ 'pages/_document',
63
+ 'pages/_app',
64
+ route
65
+ ];
66
+ const currentRouteIndex = routes.indexOf(route);
67
+ return routes.filter((_, index)=>currentRouteIndex > index).map((x)=>output.get(nameToChunk[x])).filter(Boolean).map((x)=>copy(x.usedIds));
68
+ }
69
+ const parts = route.split('/');
70
+ const parents = [];
71
+ let currentPart = '';
72
+ for (const part of parts){
73
+ currentPart = [
74
+ currentPart,
75
+ part
76
+ ].filter(Boolean).join('/');
77
+ const possibleParent = `${currentPart}/layout`;
78
+ if (route === possibleParent) {
79
+ continue;
80
+ }
81
+ const parentChunk = nameToChunk[possibleParent];
82
+ if (output.has(parentChunk)) {
83
+ parents.push(copy(output.get(parentChunk).usedIds));
84
+ }
85
+ }
86
+ return parents;
87
+ };
88
+ const newOutput = new Map();
89
+ for (const chunk of output.keys()){
90
+ const currentUsedIds = copy(output.get(chunk).usedIds);
91
+ for (const parent of getAllParentUsedIds(chunk)){
92
+ removeParentFromCurrent(currentUsedIds, parent);
93
+ }
94
+ newOutput.set(chunk, {
95
+ ...output.get(chunk),
96
+ usedIds: currentUsedIds
97
+ });
98
+ }
99
+ return newOutput;
100
+ }
101
+
102
+ const MiniCssExtractPlugin = NextMiniCssExtractPlugin['default'];
103
+ const createNavitaStylePlugin = (navitaConfig = {})=>(nextConfig)=>Object.assign({}, nextConfig, {
104
+ webpack (config, options) {
105
+ const { dir , config: resolvedNextConfig , dev } = options;
106
+ config.plugins?.push({
107
+ apply (compiler) {
108
+ // We call the getNavitaModule function here
109
+ // so that NavitaModule is created with what's required for the next.js rsc to client entry promotion:
110
+ webpackPlugin.getNavitaModule(compiler.webpack, ({ cssHash , issuerPath })=>({
111
+ // The resourceResolveData is used by next.js to promote server and ssr entries to client entries:
112
+ // https://github.com/vercel/next.js/blob/f3132354285fb18c290bf9aad7f8dc7e0550105d/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts#L590
113
+ resourceResolveData: {
114
+ path: '',
115
+ query: '?' + new URLSearchParams({
116
+ cssHash,
117
+ issuerPath
118
+ }).toString()
119
+ },
120
+ // We set the resource to ".css"
121
+ // to trick next.js into thinking this is a css module:
122
+ // https://github.com/vercel/next.js/blob/f3132354285fb18c290bf9aad7f8dc7e0550105d/packages/next/src/build/webpack/loaders/utils.ts#L24
123
+ resource: '.css'
124
+ }));
125
+ }
126
+ });
127
+ // This loader promotes the server and ssr entries to client entries:
128
+ config.module?.rules.unshift({
129
+ resourceQuery: [
130
+ /cssHash/,
131
+ /issuerPath/
132
+ ],
133
+ loader: require.resolve("@navita/next-plugin/fromServerLoader")
134
+ });
135
+ const findPagesDirResult = findPagesDir.findPagesDir(dir, !!resolvedNextConfig.experimental.appDir);
136
+ const hasAppDir = !!(findPagesDirResult && findPagesDirResult.appDir);
137
+ const isServer = options.isServer && !(options.nextRuntime === 'edge');
138
+ const outputCss = !isServer || hasAppDir;
139
+ if (!hasAppDir && !isServer) {
140
+ const filename = dev ? 'static/css/[name].css' : 'static/css/[contenthash].css';
141
+ // https://github.com/vercel/next.js/blob/930db5c1afbe541a0b2357c26123c2b365b56624/packages/next/src/build/webpack/config/blocks/css/index.ts#L595
142
+ config.plugins.push(new MiniCssExtractPlugin({
143
+ filename,
144
+ chunkFilename: filename,
145
+ ignoreOrder: true
146
+ }));
147
+ }
148
+ if (navitaConfig?.singleCssFile) {
149
+ config.optimization.splitChunks = {
150
+ ...config.optimization.splitChunks || {},
151
+ cacheGroups: {
152
+ ...config.optimization.splitChunks['cacheGroups'] || {},
153
+ navita: {
154
+ chunks: 'all',
155
+ enforce: true,
156
+ name: 'navita',
157
+ type: webpackPlugin.NAVITA_MODULE_TYPE
158
+ }
159
+ }
160
+ };
161
+ }
162
+ config.plugins?.push(new webpackPlugin.NavitaPlugin({
163
+ outputCss,
164
+ ...navitaConfig,
165
+ optimizeCSSOutput
166
+ }));
167
+ if (typeof nextConfig.webpack === "function") {
168
+ return nextConfig.webpack(config, options);
169
+ }
170
+ return config;
171
+ }
172
+ });
173
+
174
+ exports.createNavitaStylePlugin = createNavitaStylePlugin;
package/index.mjs ADDED
@@ -0,0 +1,174 @@
1
+ import { createRequire as createRequire$1 } from 'module';
2
+ const require = createRequire$1(import.meta.url);
3
+ import { getNavitaModule, NAVITA_MODULE_TYPE, NavitaPlugin } from '@navita/webpack-plugin';
4
+ import NextMiniCssExtractPlugin from 'next/dist/build/webpack/plugins/mini-css-extract-plugin';
5
+ import { findPagesDir } from 'next/dist/lib/find-pages-dir';
6
+
7
+ const merge = (...merge)=>{
8
+ const result = {};
9
+ for (const usedIds of merge){
10
+ for(const key in usedIds){
11
+ result[key] = [
12
+ ...result[key] || [],
13
+ ...usedIds[key] || []
14
+ ];
15
+ }
16
+ }
17
+ for(const key1 in result){
18
+ result[key1] = [
19
+ ...new Set(result[key1])
20
+ ];
21
+ }
22
+ return result;
23
+ };
24
+ const intersect = (...intersect)=>{
25
+ const result = {};
26
+ for (const usedIds of intersect){
27
+ for(const key in usedIds){
28
+ result[key] = result[key] ? result[key].filter((id)=>usedIds[key].includes(id)) : usedIds[key];
29
+ }
30
+ }
31
+ return result;
32
+ };
33
+ // structuredClone would be better, but since Next.js version 13 is at node 16.14.0,
34
+ // we can't use it.
35
+ const copy = (source)=>JSON.parse(JSON.stringify(source));
36
+ const removeParentFromCurrent = (usedIds, parentUsedIds)=>{
37
+ for(const key in parentUsedIds){
38
+ if (usedIds[key] && parentUsedIds[key]) {
39
+ usedIds[key] = usedIds[key].filter((id)=>!parentUsedIds[key].includes(id));
40
+ }
41
+ }
42
+ };
43
+ function optimizeCSSOutput(output) {
44
+ const nameToChunk = Object.fromEntries(Array.from(output.keys()).filter((x)=>x.name).map((x)=>[
45
+ x.name,
46
+ x
47
+ ]));
48
+ const getAllParentUsedIds = (chunk)=>{
49
+ const { name: route } = chunk;
50
+ if (!route) {
51
+ // If we don't have a name, it's a dynamic chunk.
52
+ const value = output.get(chunk);
53
+ if (value.parents.length === 0) {
54
+ return [];
55
+ }
56
+ return [
57
+ intersect(...value.parents.map((parent)=>merge(...getAllParentUsedIds(parent), output.get(parent).usedIds)))
58
+ ];
59
+ }
60
+ if (route.startsWith('pages/')) {
61
+ const routes = [
62
+ 'pages/_document',
63
+ 'pages/_app',
64
+ route
65
+ ];
66
+ const currentRouteIndex = routes.indexOf(route);
67
+ return routes.filter((_, index)=>currentRouteIndex > index).map((x)=>output.get(nameToChunk[x])).filter(Boolean).map((x)=>copy(x.usedIds));
68
+ }
69
+ const parts = route.split('/');
70
+ const parents = [];
71
+ let currentPart = '';
72
+ for (const part of parts){
73
+ currentPart = [
74
+ currentPart,
75
+ part
76
+ ].filter(Boolean).join('/');
77
+ const possibleParent = `${currentPart}/layout`;
78
+ if (route === possibleParent) {
79
+ continue;
80
+ }
81
+ const parentChunk = nameToChunk[possibleParent];
82
+ if (output.has(parentChunk)) {
83
+ parents.push(copy(output.get(parentChunk).usedIds));
84
+ }
85
+ }
86
+ return parents;
87
+ };
88
+ const newOutput = new Map();
89
+ for (const chunk of output.keys()){
90
+ const currentUsedIds = copy(output.get(chunk).usedIds);
91
+ for (const parent of getAllParentUsedIds(chunk)){
92
+ removeParentFromCurrent(currentUsedIds, parent);
93
+ }
94
+ newOutput.set(chunk, {
95
+ ...output.get(chunk),
96
+ usedIds: currentUsedIds
97
+ });
98
+ }
99
+ return newOutput;
100
+ }
101
+
102
+ const MiniCssExtractPlugin = NextMiniCssExtractPlugin['default'];
103
+ const createNavitaStylePlugin = (navitaConfig = {})=>(nextConfig)=>Object.assign({}, nextConfig, {
104
+ webpack (config, options) {
105
+ const { dir , config: resolvedNextConfig , dev } = options;
106
+ config.plugins?.push({
107
+ apply (compiler) {
108
+ // We call the getNavitaModule function here
109
+ // so that NavitaModule is created with what's required for the next.js rsc to client entry promotion:
110
+ getNavitaModule(compiler.webpack, ({ cssHash , issuerPath })=>({
111
+ // The resourceResolveData is used by next.js to promote server and ssr entries to client entries:
112
+ // https://github.com/vercel/next.js/blob/f3132354285fb18c290bf9aad7f8dc7e0550105d/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts#L590
113
+ resourceResolveData: {
114
+ path: '',
115
+ query: '?' + new URLSearchParams({
116
+ cssHash,
117
+ issuerPath
118
+ }).toString()
119
+ },
120
+ // We set the resource to ".css"
121
+ // to trick next.js into thinking this is a css module:
122
+ // https://github.com/vercel/next.js/blob/f3132354285fb18c290bf9aad7f8dc7e0550105d/packages/next/src/build/webpack/loaders/utils.ts#L24
123
+ resource: '.css'
124
+ }));
125
+ }
126
+ });
127
+ // This loader promotes the server and ssr entries to client entries:
128
+ config.module?.rules.unshift({
129
+ resourceQuery: [
130
+ /cssHash/,
131
+ /issuerPath/
132
+ ],
133
+ loader: require.resolve("@navita/next-plugin/fromServerLoader")
134
+ });
135
+ const findPagesDirResult = findPagesDir(dir, !!resolvedNextConfig.experimental.appDir);
136
+ const hasAppDir = !!(findPagesDirResult && findPagesDirResult.appDir);
137
+ const isServer = options.isServer && !(options.nextRuntime === 'edge');
138
+ const outputCss = !isServer || hasAppDir;
139
+ if (!hasAppDir && !isServer) {
140
+ const filename = dev ? 'static/css/[name].css' : 'static/css/[contenthash].css';
141
+ // https://github.com/vercel/next.js/blob/930db5c1afbe541a0b2357c26123c2b365b56624/packages/next/src/build/webpack/config/blocks/css/index.ts#L595
142
+ config.plugins.push(new MiniCssExtractPlugin({
143
+ filename,
144
+ chunkFilename: filename,
145
+ ignoreOrder: true
146
+ }));
147
+ }
148
+ if (navitaConfig?.singleCssFile) {
149
+ config.optimization.splitChunks = {
150
+ ...config.optimization.splitChunks || {},
151
+ cacheGroups: {
152
+ ...config.optimization.splitChunks['cacheGroups'] || {},
153
+ navita: {
154
+ chunks: 'all',
155
+ enforce: true,
156
+ name: 'navita',
157
+ type: NAVITA_MODULE_TYPE
158
+ }
159
+ }
160
+ };
161
+ }
162
+ config.plugins?.push(new NavitaPlugin({
163
+ outputCss,
164
+ ...navitaConfig,
165
+ optimizeCSSOutput
166
+ }));
167
+ if (typeof nextConfig.webpack === "function") {
168
+ return nextConfig.webpack(config, options);
169
+ }
170
+ return config;
171
+ }
172
+ });
173
+
174
+ export { createNavitaStylePlugin };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@navita/next-plugin",
3
+ "version": "0.0.0-main-20230917201540",
4
+ "license": "MIT",
5
+ "private": false,
6
+ "sideEffects": false,
7
+ "exports": {
8
+ ".": {
9
+ "import": "./index.mjs",
10
+ "require": "./index.js",
11
+ "types": "./index.d.ts"
12
+ },
13
+ "./fromServerLoader": {
14
+ "import": "./fromServerLoader.mjs",
15
+ "require": "./fromServerLoader.js",
16
+ "types": "./fromServerLoader.d.ts"
17
+ }
18
+ },
19
+ "dependencies": {
20
+ "@navita/webpack-plugin": "0.0.0-main-20230917201540",
21
+ "browserslist": "^4.21.5"
22
+ },
23
+ "peerDependencies": {
24
+ "next": ">=12 || >=13",
25
+ "webpack": "^5"
26
+ }
27
+ }