next-build-filter 0.2.2 → 0.2.5

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.
@@ -1,35 +1,47 @@
1
1
  /**
2
2
  * Empty module to replace filtered pages
3
- *
3
+ *
4
4
  * This module provides a custom 404 response with a detectable marker
5
5
  * that can be verified in tests and provides clear feedback to users.
6
- *
6
+ *
7
7
  * Marker: NEXT_BUILD_FILTER_EXCLUDED_PAGE
8
8
  */
9
9
 
10
+ // Marker string to identify filtered pages
11
+ const FILTER_MARKER = 'NEXT_BUILD_FILTER_EXCLUDED_PAGE';
12
+
10
13
  // Try to import notFound for App Router (will fail gracefully for Pages Router)
11
- let notFound;
14
+ let notFoundFn = null;
12
15
  try {
13
- notFound = require('next/navigation').notFound;
16
+ notFoundFn = require('next/navigation').notFound;
14
17
  } catch (e) {
15
18
  // Not in App Router context or Next.js not available
16
- notFound = null;
17
19
  }
18
20
 
19
- // Marker string to identify filtered pages
20
- const FILTER_MARKER = 'NEXT_BUILD_FILTER_EXCLUDED_PAGE';
21
+ // Import React at module level for better compatibility
22
+ let React = null;
23
+ try {
24
+ React = require('react');
25
+ } catch (e) {
26
+ // React not available
27
+ }
21
28
 
22
- // For Pages Router: Return a component that renders 404
29
+ /**
30
+ * Filtered page component
31
+ * Works with both Pages Router and App Router
32
+ */
23
33
  function FilteredPage() {
24
34
  // If we're in App Router and notFound is available, use it
25
- if (notFound && typeof notFound === 'function') {
26
- notFound();
35
+ if (notFoundFn && typeof notFoundFn === 'function') {
36
+ notFoundFn();
27
37
  return null; // notFound() throws, so this won't be reached
28
38
  }
29
-
39
+
30
40
  // For Pages Router or fallback: Return a custom 404 component
31
- // Using React.createElement to avoid JSX
32
- const React = require('react');
41
+ if (!React) {
42
+ return null;
43
+ }
44
+
33
45
  return React.createElement(
34
46
  'div',
35
47
  {
@@ -42,18 +54,28 @@ function FilteredPage() {
42
54
  fontFamily: 'system-ui, -apple-system, sans-serif',
43
55
  backgroundColor: '#f5f5f5',
44
56
  },
45
- 'data-filter-marker': FILTER_MARKER
57
+ 'data-filter-marker': FILTER_MARKER,
46
58
  },
47
59
  [
48
- React.createElement('h1', { key: 'title', style: { fontSize: '3rem', margin: '0' } }, '404'),
60
+ React.createElement(
61
+ 'h1',
62
+ { key: 'title', style: { fontSize: '3rem', margin: '0' } },
63
+ '404'
64
+ ),
49
65
  React.createElement(
50
66
  'p',
51
- { key: 'message', style: { fontSize: '1.2rem', color: '#666', marginTop: '1rem' } },
67
+ {
68
+ key: 'message',
69
+ style: { fontSize: '1.2rem', color: '#666', marginTop: '1rem' },
70
+ },
52
71
  'Page Not Available'
53
72
  ),
54
73
  React.createElement(
55
74
  'p',
56
- { key: 'detail', style: { fontSize: '0.9rem', color: '#999', marginTop: '0.5rem' } },
75
+ {
76
+ key: 'detail',
77
+ style: { fontSize: '0.9rem', color: '#999', marginTop: '0.5rem' },
78
+ },
57
79
  'This page has been filtered out during build.'
58
80
  ),
59
81
  React.createElement(
@@ -80,15 +102,16 @@ module.exports.default = FilteredPage;
80
102
  // Export the marker for testing
81
103
  module.exports.FILTER_MARKER = FILTER_MARKER;
82
104
 
83
- // Generate metadata for App Router if needed
84
- module.exports.generateMetadata = () => ({
105
+ // Generate metadata for App Router (async for Next.js best practices)
106
+ module.exports.generateMetadata = async () => ({
85
107
  title: 'Page Not Available - 404',
86
108
  description: 'This page has been filtered out during build',
87
109
  });
88
110
 
89
111
  // Generate static params for dynamic routes if needed
90
- module.exports.generateStaticParams = () => [];
112
+ module.exports.generateStaticParams = async () => [];
91
113
 
92
114
  // Export all common App Router functions
93
- module.exports.generateViewport = () => ({});
94
- module.exports.revalidate = false;
115
+ module.exports.generateViewport = async () => ({});
116
+ module.exports.revalidate = false;
117
+ module.exports.dynamic = 'force-static';
@@ -1,6 +1,5 @@
1
- const path = require('path');
2
- const fs = require('fs');
3
1
  const { minimatch } = require('minimatch');
2
+ const path = require('path');
4
3
 
5
4
  /**
6
5
  * Next.js Build Filter Plugin
@@ -35,6 +34,13 @@ class NextBuildFilterPlugin {
35
34
  supportAppRouter: options.supportAppRouter !== false,
36
35
  supportPagesRouter: options.supportPagesRouter !== false
37
36
  };
37
+
38
+ // Resolve the empty module path once and store it
39
+ // This ensures it resolves correctly regardless of where the plugin is loaded from
40
+ this.emptyModulePath = path.resolve(__dirname, 'empty-module.js');
41
+
42
+ // Store original Module._resolveFilename if we need to intercept Node.js module resolution
43
+ this.originalResolveFilename = null;
38
44
  }
39
45
 
40
46
  apply(compiler) {
@@ -47,6 +53,11 @@ class NextBuildFilterPlugin {
47
53
 
48
54
  const pluginName = 'NextBuildFilterPlugin';
49
55
 
56
+ // Intercept Node.js module resolution to catch Next.js requirePage calls
57
+ // This is needed because Next.js uses requirePage which bypasses webpack
58
+ // Note: This is a workaround for Next.js's page collection phase
59
+ this.setupModuleResolutionInterceptor();
60
+
50
61
  // Hook into the compilation process
51
62
  compiler.hooks.beforeCompile.tapAsync(pluginName, (params, callback) => {
52
63
  if (this.options.verbose) {
@@ -57,6 +68,8 @@ class NextBuildFilterPlugin {
57
68
 
58
69
  // Filter out pages during the resolve phase
59
70
  compiler.hooks.normalModuleFactory.tap(pluginName, (normalModuleFactory) => {
71
+ // Hook into beforeResolve to intercept page module requests
72
+ // Note: beforeResolve is a bailing hook - modify resolveData in place, don't return it
60
73
  normalModuleFactory.hooks.beforeResolve.tap(pluginName, (resolveData) => {
61
74
  if (!resolveData || !resolveData.request) {
62
75
  return;
@@ -81,33 +94,75 @@ class NextBuildFilterPlugin {
81
94
  }
82
95
 
83
96
  // Replace with empty module instead of blocking the request
84
- resolveData.request = require.resolve('./empty-module.js');
85
- return;
97
+ // Use the pre-resolved absolute path to ensure it works in all contexts
98
+ // Modify in place - don't return the object
99
+ resolveData.request = this.emptyModulePath;
100
+ return; // Continue processing with modified resolveData
86
101
  }
87
102
  }
88
103
 
89
- // Don't return resolveData, just let it continue processing
104
+ // Don't return anything - let webpack continue with the original resolveData
90
105
  });
91
- });
106
+
107
+ // Also hook into afterResolve as a fallback
108
+ // Note: afterResolve is also a bailing hook
109
+ normalModuleFactory.hooks.afterResolve.tap(pluginName, (resolveData) => {
110
+ if (!resolveData || !resolveData.request) {
111
+ return;
112
+ }
92
113
 
93
- // Additional hook to filter entries
94
- compiler.hooks.entryOption.tap(pluginName, (context, entry) => {
95
- if (typeof entry === 'object' && !Array.isArray(entry)) {
96
- const filteredEntry = {};
114
+ const request = resolveData.request;
115
+
116
+ if (typeof request !== 'string') {
117
+ return;
118
+ }
97
119
 
98
- for (const [key, value] of Object.entries(entry)) {
99
- if (!this.shouldExcludeEntryPoint(key)) {
100
- filteredEntry[key] = value;
101
- } else if (this.options.verbose) {
102
- console.log(`📄 NextBuildFilterPlugin: Excluding entry point: ${key}`);
120
+ // Check if this is a page file that should be excluded
121
+ if (this.isPageFile(request, resolveData.context)) {
122
+ const normalizedRequest = this.normalizePath(request);
123
+ const routePath = this.extractRoutePath(normalizedRequest);
124
+ const shouldExclude = this.shouldExcludePage(request);
125
+
126
+ if (shouldExclude && request !== this.emptyModulePath) {
127
+ if (this.options.verbose) {
128
+ console.log(`📄 Filtering out (afterResolve): ${routePath || request}`);
129
+ }
130
+
131
+ // Replace with empty module - modify in place
132
+ resolveData.request = this.emptyModulePath;
133
+ return; // Continue processing with modified resolveData
103
134
  }
104
135
  }
105
136
 
106
- // Replace the entry object with filtered version
107
- Object.keys(entry).forEach(key => delete entry[key]);
108
- Object.assign(entry, filteredEntry);
109
- }
137
+ // Don't return anything - let webpack continue
138
+ });
110
139
  });
140
+
141
+ // Hook into compilation to intercept module building
142
+ compiler.hooks.compilation.tap(pluginName, (compilation) => {
143
+ compilation.hooks.buildModule.tap(pluginName, (module) => {
144
+ if (module.resource && typeof module.resource === 'string') {
145
+ if (this.isPageFile(module.resource, module.context)) {
146
+ const normalizedRequest = this.normalizePath(module.resource);
147
+ const routePath = this.extractRoutePath(normalizedRequest);
148
+ const shouldExclude = this.shouldExcludePage(module.resource);
149
+
150
+ if (shouldExclude && module.resource !== this.emptyModulePath) {
151
+ if (this.options.verbose) {
152
+ console.log(`📄 Filtering module during build: ${routePath || module.resource}`);
153
+ }
154
+ // Mark module as filtered - this prevents it from being processed
155
+ module._filtered = true;
156
+ }
157
+ }
158
+ }
159
+ });
160
+ });
161
+
162
+ // NOTE: We intentionally do NOT filter entries here because Next.js still needs
163
+ // compiled modules to exist for page data collection. Instead, we let all entries
164
+ // compile but replace their content with empty modules via beforeResolve/afterResolve hooks.
165
+ // This ensures Next.js can find and load the modules, but they'll be empty stubs.
111
166
  }
112
167
 
113
168
  /**
@@ -251,16 +306,21 @@ class NextBuildFilterPlugin {
251
306
  routePath = routePath.replace(/\/?page\.(tsx|ts|jsx|js)$/, '');
252
307
  // Remove leading slash if present
253
308
  routePath = routePath.replace(/^\//, '');
309
+ // Handle route groups - remove (group) patterns like (auth), (admin)
310
+ routePath = routePath.replace(/\/?\([^)]+\)/g, '');
254
311
  } else {
255
312
  // For Pages Router, remove extension
256
313
  routePath = routePath.replace(/\.(tsx|ts|jsx|js)$/, '');
257
314
  }
258
-
315
+
316
+ // Clean up empty segments and normalize
317
+ routePath = routePath.replace(/\/+/g, '/').replace(/^\/|\/$/g, '');
318
+
259
319
  // Handle root route
260
320
  if (!routePath || routePath === '') {
261
321
  return 'index';
262
322
  }
263
-
323
+
264
324
  return routePath;
265
325
  }
266
326
 
@@ -269,15 +329,19 @@ class NextBuildFilterPlugin {
269
329
  */
270
330
  shouldExcludeEntryPoint(entryKey) {
271
331
  // Entry keys in Next.js typically follow patterns like:
272
- // - pages/index
273
- // - pages/about
274
- // - pages/api/users
275
-
276
- if (entryKey.startsWith('pages/')) {
332
+ // Pages Router: pages/index, pages/about, pages/api/users
333
+ // App Router: app/page, app/about/page, app/admin/users/page
334
+
335
+ if (this.options.supportPagesRouter && entryKey.startsWith('pages/')) {
277
336
  const pagePath = entryKey.replace('pages/', '');
278
- return this.shouldExcludePage(pagePath);
337
+ return this.shouldExcludePage(`/${this.options.pagesDir}/${pagePath}.js`);
279
338
  }
280
-
339
+
340
+ if (this.options.supportAppRouter && entryKey.startsWith('app/')) {
341
+ const pagePath = entryKey.replace('app/', '');
342
+ return this.shouldExcludePage(`/${this.options.appDir}/${pagePath}.js`);
343
+ }
344
+
281
345
  return false;
282
346
  }
283
347
 
@@ -290,6 +354,114 @@ class NextBuildFilterPlugin {
290
354
  }
291
355
  return filePath.replace(/\\/g, '/').toLowerCase();
292
356
  }
357
+
358
+ /**
359
+ * Setup Node.js module resolution interceptor to catch Next.js requirePage calls
360
+ * This is needed because Next.js uses requirePage which bypasses webpack
361
+ */
362
+ setupModuleResolutionInterceptor() {
363
+ const Module = require('module');
364
+
365
+ // Only intercept if not already intercepted
366
+ if (this.originalResolveFilename) {
367
+ return;
368
+ }
369
+
370
+ // Store the original resolveFilename
371
+ this.originalResolveFilename = Module._resolveFilename;
372
+ const self = this;
373
+
374
+ // Intercept module resolution
375
+ Module._resolveFilename = function(request, parent, isMain, options) {
376
+ // Check if this request might be for a filtered page before trying to resolve
377
+ // Next.js requirePage uses paths like "/admin" or paths to page files
378
+ const normalizedRequest = self.normalizePath(request);
379
+ let routePath = self.extractRoutePath(normalizedRequest);
380
+
381
+ // If we can't extract route path from request, try to infer it
382
+ // Next.js might be looking for pages like "/admin" or "/dev/debug"
383
+ if (!routePath && request && typeof request === 'string') {
384
+ // Check if request looks like a Next.js page path (starts with / but not /node_modules)
385
+ if (request.startsWith('/') && !request.startsWith('/node_modules') && !request.includes('node_modules')) {
386
+ routePath = request.replace(/^\//, '').replace(/\/$/, '');
387
+ if (!routePath) routePath = 'index';
388
+ }
389
+ }
390
+
391
+ // Check if this route should be excluded
392
+ if (routePath) {
393
+ // Try multiple path formats that Next.js might use
394
+ const testPaths = [
395
+ request,
396
+ `/${self.options.pagesDir}/${routePath}.js`,
397
+ `/${self.options.pagesDir}/${routePath}.jsx`,
398
+ `/${self.options.pagesDir}/${routePath}.ts`,
399
+ `/${self.options.pagesDir}/${routePath}.tsx`,
400
+ `/${self.options.appDir}/${routePath}/page.js`,
401
+ `/${self.options.appDir}/${routePath}/page.jsx`,
402
+ `/${self.options.appDir}/${routePath}/page.ts`,
403
+ `/${self.options.appDir}/${routePath}/page.tsx`,
404
+ routePath,
405
+ `/${routePath}`,
406
+ ];
407
+
408
+ const shouldExclude = testPaths.some(testPath => self.shouldExcludePage(testPath));
409
+
410
+ if (shouldExclude) {
411
+ if (self.options.verbose) {
412
+ console.log(`📄 Intercepting module resolution for filtered page: ${routePath} (request: ${request})`);
413
+ }
414
+ // Return the empty module path instead
415
+ return self.emptyModulePath;
416
+ }
417
+ }
418
+
419
+ // Try to resolve normally
420
+ try {
421
+ const resolvedPath = self.originalResolveFilename.call(this, request, parent, isMain, options);
422
+
423
+ // Check if the resolved path is a page file that should be excluded
424
+ if (resolvedPath && self.isPageFile(resolvedPath, parent ? parent.filename : null)) {
425
+ const normalizedResolved = self.normalizePath(resolvedPath);
426
+ const resolvedRoutePath = self.extractRoutePath(normalizedResolved);
427
+ const shouldExclude = self.shouldExcludePage(resolvedPath);
428
+
429
+ if (shouldExclude) {
430
+ if (self.options.verbose) {
431
+ console.log(`📄 Intercepting resolved module for filtered page: ${resolvedRoutePath || resolvedPath}`);
432
+ }
433
+ // Return the empty module path instead
434
+ return self.emptyModulePath;
435
+ }
436
+ }
437
+
438
+ return resolvedPath;
439
+ } catch (e) {
440
+ // If resolution fails and it's a filtered page, return empty module
441
+ if (routePath) {
442
+ const testPaths = [
443
+ request,
444
+ `/${self.options.pagesDir}/${routePath}.js`,
445
+ `/${self.options.appDir}/${routePath}/page.js`,
446
+ routePath,
447
+ `/${routePath}`,
448
+ ];
449
+
450
+ const shouldExclude = testPaths.some(testPath => self.shouldExcludePage(testPath));
451
+
452
+ if (shouldExclude) {
453
+ if (self.options.verbose) {
454
+ console.log(`📄 Intercepting failed resolution for filtered page: ${routePath} (request: ${request})`);
455
+ }
456
+ return self.emptyModulePath;
457
+ }
458
+ }
459
+
460
+ // Re-throw the original error if it's not a filtered page
461
+ throw e;
462
+ }
463
+ };
464
+ }
293
465
  }
294
466
 
295
467
  module.exports = NextBuildFilterPlugin;
@@ -1,25 +1,23 @@
1
- const path = require('path');
2
- const { minimatch } = require('minimatch');
3
-
4
1
  /**
5
2
  * Next.js Page Filter Plugin
6
- *
3
+ *
7
4
  * Enhanced version that supports both Pages Router and App Router (Next.js 13+)
8
5
  * Supports glob patterns for includedPages and excludedPages options
9
6
  */
10
7
  function withPageFilter(options = {}) {
11
8
  return (nextConfig = {}) => {
12
9
  const filterOptions = {
13
- includedPages: options.includedPages || [],
14
- excludedPages: options.excludedPages || [],
15
- enabled: options.enabled !== undefined ? options.enabled : process.env.FILTER_PAGES === 'true',
16
- excludePatterns: options.excludePatterns || [],
17
- verbose: options.verbose || false,
18
- pagesDir: options.pagesDir || 'pages',
19
- appDir: options.appDir || 'app',
10
+ // Spread options first, then apply defaults for undefined values
11
+ ...options,
12
+ includedPages: options.includedPages ?? [],
13
+ excludedPages: options.excludedPages ?? [],
14
+ enabled: options.enabled ?? process.env.FILTER_PAGES === 'true',
15
+ excludePatterns: options.excludePatterns ?? [],
16
+ verbose: options.verbose ?? false,
17
+ pagesDir: options.pagesDir ?? 'pages',
18
+ appDir: options.appDir ?? 'app',
20
19
  supportAppRouter: options.supportAppRouter !== false, // Default to true
21
20
  supportPagesRouter: options.supportPagesRouter !== false, // Default to true
22
- ...options
23
21
  };
24
22
 
25
23
  if (!filterOptions.enabled) {
@@ -32,14 +30,14 @@ function withPageFilter(options = {}) {
32
30
  return {
33
31
  ...nextConfig,
34
32
 
35
- webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
33
+ webpack: (config, options) => {
36
34
  // Apply the existing webpack config if it exists
37
35
  if (nextConfig.webpack) {
38
- config = nextConfig.webpack(config, { buildId, dev, isServer, defaultLoaders, webpack });
36
+ config = nextConfig.webpack(config, options);
39
37
  }
40
38
 
41
39
  // Don't apply filtering in development mode unless explicitly enabled
42
- if (dev && !filterOptions.enableInDev) {
40
+ if (options.dev && !filterOptions.enableInDev) {
43
41
  return config;
44
42
  }
45
43
 
@@ -58,127 +56,4 @@ function withPageFilter(options = {}) {
58
56
  };
59
57
  }
60
58
 
61
- function isPageFile(request, context, options) {
62
- if (!request) return false;
63
-
64
- const pageExtensions = ['.js', '.jsx', '.ts', '.tsx'];
65
- const hasPageExtension = pageExtensions.some(ext => request.endsWith(ext));
66
-
67
- if (!hasPageExtension) return false;
68
-
69
- // Check for Pages Router
70
- const isInPagesDir = options.supportPagesRouter && (
71
- request.includes(`/${options.pagesDir}/`) ||
72
- (context && context.includes(`/${options.pagesDir}/`))
73
- );
74
-
75
- // Check for App Router
76
- const isInAppDir = options.supportAppRouter && (
77
- request.includes(`/${options.appDir}/`) ||
78
- (context && context.includes(`/${options.appDir}/`))
79
- );
80
-
81
- return isInPagesDir || isInAppDir;
82
- }
83
-
84
- function shouldExcludePage(request, options) {
85
- const normalizedRequest = normalizePath(request);
86
-
87
- // Extract route path from request
88
- const routePath = extractRoutePath(normalizedRequest, options);
89
-
90
- if (!routePath) return false;
91
-
92
- // If includedPages is specified, only include those pages
93
- if (options.includedPages && options.includedPages.length > 0) {
94
- const isIncluded = options.includedPages.some(page => {
95
- const normalizedPage = normalizePath(page);
96
- // Try glob matching first
97
- if (minimatch(routePath, normalizedPage)) {
98
- return true;
99
- }
100
- // Fallback to exact match or substring match for backward compatibility
101
- return routePath === normalizedPage || routePath.includes(normalizedPage);
102
- });
103
- return !isIncluded;
104
- }
105
-
106
- // Check excluded pages
107
- if (options.excludedPages && options.excludedPages.length > 0) {
108
- const isExcluded = options.excludedPages.some(page => {
109
- const normalizedPage = normalizePath(page);
110
- // Try glob matching first
111
- if (minimatch(routePath, normalizedPage)) {
112
- return true;
113
- }
114
- // Fallback to exact match or substring match for backward compatibility
115
- return routePath === normalizedPage || routePath.includes(normalizedPage);
116
- });
117
- if (isExcluded) return true;
118
- }
119
-
120
- // Check exclude patterns (regex patterns)
121
- if (options.excludePatterns && options.excludePatterns.length > 0) {
122
- const isPatternExcluded = options.excludePatterns.some(pattern => {
123
- try {
124
- const regex = new RegExp(pattern);
125
- return regex.test(routePath);
126
- } catch (e) {
127
- console.warn(`📄 Invalid regex pattern: ${pattern}`);
128
- return false;
129
- }
130
- });
131
- if (isPatternExcluded) return true;
132
- }
133
-
134
- return false;
135
- }
136
-
137
- function extractRoutePath(normalizedRequest, options) {
138
- // Extract route path from either /pages/ or /app/ directory
139
- let routePath = null;
140
-
141
- // Check for Pages Router path
142
- if (options.supportPagesRouter) {
143
- const pagesIndex = normalizedRequest.indexOf(`/${options.pagesDir}/`);
144
- if (pagesIndex !== -1) {
145
- routePath = normalizedRequest.substring(pagesIndex + `/${options.pagesDir}/`.length);
146
- }
147
- }
148
-
149
- // Check for App Router path
150
- if (!routePath && options.supportAppRouter) {
151
- const appIndex = normalizedRequest.indexOf(`/${options.appDir}/`);
152
- if (appIndex !== -1) {
153
- routePath = normalizedRequest.substring(appIndex + `/${options.appDir}/`.length);
154
- }
155
- }
156
-
157
- if (!routePath) return null;
158
-
159
- // Remove file extension
160
- routePath = routePath.replace(/\.(js|jsx|ts|tsx)$/, '');
161
-
162
- // Handle special App Router files
163
- if (options.supportAppRouter) {
164
- // Remove App Router special files (page.tsx, layout.tsx, etc.)
165
- routePath = routePath.replace(/\/(page|layout|loading|error|not-found|template|default)$/, '');
166
-
167
- // Handle route groups - remove (group) patterns
168
- routePath = routePath.replace(/\/\([^)]+\)/g, '');
169
- }
170
-
171
- // Clean up empty segments and normalize
172
- routePath = routePath.replace(/\/+/g, '/').replace(/^\/|\/$/g, '');
173
-
174
- return routePath || 'index';
175
- }
176
-
177
- function normalizePath(filePath) {
178
- if (!filePath || typeof filePath !== 'string') {
179
- return '';
180
- }
181
- return filePath.replace(/\\/g, '/').toLowerCase();
182
- }
183
-
184
59
  module.exports = withPageFilter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-build-filter",
3
- "version": "0.2.2",
3
+ "version": "0.2.5",
4
4
  "description": "A Next.js plugin to exclude pages/routes during build without removing files. Supports both Pages Router and App Router (Next.js 13+).",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -17,7 +17,7 @@
17
17
  "test:e2e": "node tests/e2e/run-e2e-tests.js",
18
18
  "test:all": "npm run test && npm run test:e2e",
19
19
  "prepublishOnly": "npm run build",
20
- "postversion": "npm publish",
20
+ "postversion": "npm run test:all && npm publish && git push --follow-tags",
21
21
  "build": "echo \"No build process needed for now\""
22
22
  },
23
23
  "keywords": [