@navita/next-plugin 0.0.0

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.
Files changed (4) hide show
  1. package/index.d.ts +7 -0
  2. package/index.js +143 -0
  3. package/index.mjs +143 -0
  4. package/package.json +17 -0
package/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { NavitaPlugin } from '@navita/webpack-plugin';
2
+ import { NextConfig } from 'next';
3
+
4
+ type NavitaConfig = ConstructorParameters<typeof NavitaPlugin>[0];
5
+ declare const createNavitaStylePlugin: (navitaConfig?: NavitaConfig) => (nextConfig: NextConfig) => NextConfig;
6
+
7
+ export { createNavitaStylePlugin };
package/index.js ADDED
@@ -0,0 +1,143 @@
1
+ 'use strict';
2
+
3
+ var webpackPlugin = require('@navita/webpack-plugin');
4
+ var browserslist = require('browserslist');
5
+ var css = require('next/dist/build/webpack/config/blocks/css');
6
+ var client = require('next/dist/build/webpack/config/blocks/css/loaders/client');
7
+ var webpack = require('webpack');
8
+
9
+ /**
10
+ * Next.js adds a cache busting query string to document:
11
+ * https://github.com/vercel/next.js/blob/805183e3e93e15db7bc6c66837925431791a2250/packages/next/src/pages/_document.tsx#L481
12
+ * mini-css-extract-plugin checks for existing css files with an exact match:
13
+ * https://github.com/webpack-contrib/mini-css-extract-plugin/blob/2985918b79cd05cd5390e8b6d70e2009ea531521/src/index.js#L919
14
+ *
15
+ * This webpack plugins fixes that by adding a data-href attribute to the link tag, since that's also part
16
+ * of the comparison that mini-css-extract-plugin does.
17
+ */ class FixLinkTagsPlugin {
18
+ static pluginName = 'FixLinkTagsPlugin';
19
+ constructor(name = 'styles.css'){
20
+ this.name = name;
21
+ }
22
+ apply(compiler) {
23
+ const name = this.name;
24
+ class AddDataHrefToLink extends webpack.RuntimeModule {
25
+ constructor(){
26
+ super(FixLinkTagsPlugin.pluginName, webpack.RuntimeModule.STAGE_NORMAL);
27
+ }
28
+ generate() {
29
+ return webpack.Template.asString([
30
+ `const styleSheets = document.querySelectorAll('link[rel="stylesheet"][href*="${name}"]');`,
31
+ `for (const styleSheet of styleSheets) {`,
32
+ webpack.Template.indent(`styleSheet.setAttribute('data-href', (styleSheet.getAttribute('href') || '').replace(/\\?.*$/, ''));`),
33
+ `}`
34
+ ]);
35
+ }
36
+ }
37
+ compiler.hooks.thisCompilation.tap(FixLinkTagsPlugin.pluginName, (compilation)=>{
38
+ compilation.hooks.runtimeRequirementInTree.for(webpack.RuntimeGlobals.ensureChunkHandlers).tap(FixLinkTagsPlugin.pluginName, (chunk)=>{
39
+ compilation.addRuntimeModule(chunk, new AddDataHrefToLink());
40
+ });
41
+ });
42
+ }
43
+ }
44
+
45
+ function getCssLoader() {
46
+ try {
47
+ // v12+
48
+ return require.resolve('next/dist/build/webpack/loaders/css-loader/src');
49
+ } catch (_) {
50
+ return 'css-loader';
51
+ }
52
+ }
53
+
54
+ function getNextCssRules(config) {
55
+ return config.module?.rules?.find((rule)=>typeof rule === "object" && Array.isArray(rule.oneOf) && rule.oneOf.some(({ test })=>test instanceof RegExp && typeof test.test === "function" && test.test("filename.css")))?.oneOf;
56
+ }
57
+
58
+ const NextMiniCssExtractPlugin = require("next/dist/build/webpack/plugins/mini-css-extract-plugin").default;
59
+ const getSupportedBrowsers = (dir, isDevelopment)=>{
60
+ try {
61
+ return browserslist.loadConfig({
62
+ path: dir,
63
+ env: isDevelopment ? "development" : "production"
64
+ });
65
+ } catch {}
66
+ return undefined;
67
+ };
68
+ const navitaStyleConfig = (nextConfig, navitaConfig = {})=>{
69
+ return {
70
+ webpack (config, options) {
71
+ const { dir , dev , isServer } = options;
72
+ const cssRules = getNextCssRules(config);
73
+ const outputLoaders = [
74
+ {
75
+ loader: getCssLoader(),
76
+ options: {
77
+ postcss: ()=>css.lazyPostCSS(dir, getSupportedBrowsers(dir, dev), undefined)
78
+ }
79
+ }
80
+ ];
81
+ if (!isServer) {
82
+ const styleLoader = client.getClientStyleLoader({
83
+ // False to force the use of MiniCssExtractPlugin
84
+ isDevelopment: false,
85
+ assetPrefix: options.config.assetPrefix,
86
+ hasAppDir: false,
87
+ isAppDir: false
88
+ });
89
+ outputLoaders.unshift(styleLoader);
90
+ }
91
+ cssRules?.unshift({
92
+ test: webpackPlugin.getEmptyCssFilePath(),
93
+ sideEffects: true,
94
+ use: outputLoaders
95
+ });
96
+ // Add next's MiniCssExtractPlugin if it's not already added
97
+ // todo: ^- only on development?
98
+ if (!config.plugins?.some((plugin)=>plugin instanceof NextMiniCssExtractPlugin)) {
99
+ // HMR reloads the CSS file when the content changes but does not use
100
+ // the new file name, which means it can't contain a hash.
101
+ // Logic adopted from https://git.io/JtdBy
102
+ config.plugins?.push(new NextMiniCssExtractPlugin({
103
+ filename: "static/css/[name].css"
104
+ }));
105
+ }
106
+ config.plugins?.push(new webpackPlugin.NavitaPlugin({
107
+ ...navitaConfig,
108
+ outputCss: !isServer,
109
+ isServer
110
+ }));
111
+ if (!isServer && dev) {
112
+ config.plugins?.push(new FixLinkTagsPlugin());
113
+ }
114
+ if (typeof nextConfig.webpack === "function") {
115
+ return nextConfig.webpack(config, options);
116
+ }
117
+ config.optimization = {
118
+ ...config.optimization,
119
+ splitChunks: {
120
+ ...config.optimization?.splitChunks,
121
+ cacheGroups: {
122
+ ...config.optimization?.splitChunks ? config.optimization.splitChunks.cacheGroups : {},
123
+ styles: {
124
+ name: 'styles',
125
+ type: 'css/mini-extract',
126
+ test: webpackPlugin.getEmptyCssFilePath(),
127
+ chunks: 'all',
128
+ enforce: true
129
+ }
130
+ }
131
+ }
132
+ };
133
+ return config;
134
+ }
135
+ };
136
+ };
137
+ const createNavitaStylePlugin = (navitaConfig = {})=>{
138
+ return (nextConfig)=>{
139
+ return Object.assign({}, nextConfig, navitaStyleConfig(nextConfig, navitaConfig));
140
+ };
141
+ };
142
+
143
+ exports.createNavitaStylePlugin = createNavitaStylePlugin;
package/index.mjs ADDED
@@ -0,0 +1,143 @@
1
+ import { createRequire as createRequire$1 } from 'module';
2
+ const require = createRequire$1(import.meta.url);
3
+ import { getEmptyCssFilePath, NavitaPlugin } from '@navita/webpack-plugin';
4
+ import { loadConfig } from 'browserslist';
5
+ import { lazyPostCSS } from 'next/dist/build/webpack/config/blocks/css';
6
+ import { getClientStyleLoader } from 'next/dist/build/webpack/config/blocks/css/loaders/client';
7
+ import { RuntimeGlobals, RuntimeModule, Template } from 'webpack';
8
+
9
+ /**
10
+ * Next.js adds a cache busting query string to document:
11
+ * https://github.com/vercel/next.js/blob/805183e3e93e15db7bc6c66837925431791a2250/packages/next/src/pages/_document.tsx#L481
12
+ * mini-css-extract-plugin checks for existing css files with an exact match:
13
+ * https://github.com/webpack-contrib/mini-css-extract-plugin/blob/2985918b79cd05cd5390e8b6d70e2009ea531521/src/index.js#L919
14
+ *
15
+ * This webpack plugins fixes that by adding a data-href attribute to the link tag, since that's also part
16
+ * of the comparison that mini-css-extract-plugin does.
17
+ */ class FixLinkTagsPlugin {
18
+ static pluginName = 'FixLinkTagsPlugin';
19
+ constructor(name = 'styles.css'){
20
+ this.name = name;
21
+ }
22
+ apply(compiler) {
23
+ const name = this.name;
24
+ class AddDataHrefToLink extends RuntimeModule {
25
+ constructor(){
26
+ super(FixLinkTagsPlugin.pluginName, RuntimeModule.STAGE_NORMAL);
27
+ }
28
+ generate() {
29
+ return Template.asString([
30
+ `const styleSheets = document.querySelectorAll('link[rel="stylesheet"][href*="${name}"]');`,
31
+ `for (const styleSheet of styleSheets) {`,
32
+ Template.indent(`styleSheet.setAttribute('data-href', (styleSheet.getAttribute('href') || '').replace(/\\?.*$/, ''));`),
33
+ `}`
34
+ ]);
35
+ }
36
+ }
37
+ compiler.hooks.thisCompilation.tap(FixLinkTagsPlugin.pluginName, (compilation)=>{
38
+ compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.ensureChunkHandlers).tap(FixLinkTagsPlugin.pluginName, (chunk)=>{
39
+ compilation.addRuntimeModule(chunk, new AddDataHrefToLink());
40
+ });
41
+ });
42
+ }
43
+ }
44
+
45
+ function getCssLoader() {
46
+ try {
47
+ // v12+
48
+ return require.resolve('next/dist/build/webpack/loaders/css-loader/src');
49
+ } catch (_) {
50
+ return 'css-loader';
51
+ }
52
+ }
53
+
54
+ function getNextCssRules(config) {
55
+ return config.module?.rules?.find((rule)=>typeof rule === "object" && Array.isArray(rule.oneOf) && rule.oneOf.some(({ test })=>test instanceof RegExp && typeof test.test === "function" && test.test("filename.css")))?.oneOf;
56
+ }
57
+
58
+ const NextMiniCssExtractPlugin = require("next/dist/build/webpack/plugins/mini-css-extract-plugin").default;
59
+ const getSupportedBrowsers = (dir, isDevelopment)=>{
60
+ try {
61
+ return loadConfig({
62
+ path: dir,
63
+ env: isDevelopment ? "development" : "production"
64
+ });
65
+ } catch {}
66
+ return undefined;
67
+ };
68
+ const navitaStyleConfig = (nextConfig, navitaConfig = {})=>{
69
+ return {
70
+ webpack (config, options) {
71
+ const { dir , dev , isServer } = options;
72
+ const cssRules = getNextCssRules(config);
73
+ const outputLoaders = [
74
+ {
75
+ loader: getCssLoader(),
76
+ options: {
77
+ postcss: ()=>lazyPostCSS(dir, getSupportedBrowsers(dir, dev), undefined)
78
+ }
79
+ }
80
+ ];
81
+ if (!isServer) {
82
+ const styleLoader = getClientStyleLoader({
83
+ // False to force the use of MiniCssExtractPlugin
84
+ isDevelopment: false,
85
+ assetPrefix: options.config.assetPrefix,
86
+ hasAppDir: false,
87
+ isAppDir: false
88
+ });
89
+ outputLoaders.unshift(styleLoader);
90
+ }
91
+ cssRules?.unshift({
92
+ test: getEmptyCssFilePath(),
93
+ sideEffects: true,
94
+ use: outputLoaders
95
+ });
96
+ // Add next's MiniCssExtractPlugin if it's not already added
97
+ // todo: ^- only on development?
98
+ if (!config.plugins?.some((plugin)=>plugin instanceof NextMiniCssExtractPlugin)) {
99
+ // HMR reloads the CSS file when the content changes but does not use
100
+ // the new file name, which means it can't contain a hash.
101
+ // Logic adopted from https://git.io/JtdBy
102
+ config.plugins?.push(new NextMiniCssExtractPlugin({
103
+ filename: "static/css/[name].css"
104
+ }));
105
+ }
106
+ config.plugins?.push(new NavitaPlugin({
107
+ ...navitaConfig,
108
+ outputCss: !isServer,
109
+ isServer
110
+ }));
111
+ if (!isServer && dev) {
112
+ config.plugins?.push(new FixLinkTagsPlugin());
113
+ }
114
+ if (typeof nextConfig.webpack === "function") {
115
+ return nextConfig.webpack(config, options);
116
+ }
117
+ config.optimization = {
118
+ ...config.optimization,
119
+ splitChunks: {
120
+ ...config.optimization?.splitChunks,
121
+ cacheGroups: {
122
+ ...config.optimization?.splitChunks ? config.optimization.splitChunks.cacheGroups : {},
123
+ styles: {
124
+ name: 'styles',
125
+ type: 'css/mini-extract',
126
+ test: getEmptyCssFilePath(),
127
+ chunks: 'all',
128
+ enforce: true
129
+ }
130
+ }
131
+ }
132
+ };
133
+ return config;
134
+ }
135
+ };
136
+ };
137
+ const createNavitaStylePlugin = (navitaConfig = {})=>{
138
+ return (nextConfig)=>{
139
+ return Object.assign({}, nextConfig, navitaStyleConfig(nextConfig, navitaConfig));
140
+ };
141
+ };
142
+
143
+ export { createNavitaStylePlugin };
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@navita/next-plugin",
3
+ "version": "0.0.0",
4
+ "exports": {
5
+ "import": "./index.mjs",
6
+ "require": "./index.js",
7
+ "types": "./index.d.ts"
8
+ },
9
+ "dependencies": {
10
+ "@navita/webpack-plugin": "*",
11
+ "browserslist": "^4.21.5"
12
+ },
13
+ "peerDependencies": {
14
+ "next": ">=12 || >=13",
15
+ "webpack": "^5"
16
+ }
17
+ }