-
Notifications
You must be signed in to change notification settings - Fork 1
Promotes the current **develop** line to **main**, including security-focused dependency work, test-sandbox alignment, packaged postinstall fix, Next.js dev Webpack fallback, and release **0.17.0-beta.1** (via chore(release) on develop).
#117
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
Changes from all commits
5841aa8
5940f31
dd86d49
12309bf
97f0178
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,30 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||
| * The `npm` package vendors dependencies (bundleDependencies). npm overrides do not | ||||||||||||||||||||||||||||||||||||||||||||||
| * replace those copies, so `npm audit` still flags known-fixed versions that exist | ||||||||||||||||||||||||||||||||||||||||||||||
| * hoisted at the project root. Sync patched trees into npm's bundle after install. | ||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||
| import { cpSync, existsSync, rmSync } from "node:fs"; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { dirname, join } from "node:path"; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { fileURLToPath } from "node:url"; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const root = join(dirname(fileURLToPath(import.meta.url)), ".."); | ||||||||||||||||||||||||||||||||||||||||||||||
| const npmRoot = join(root, "node_modules", "npm"); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| function replaceDir(src, dest) { | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!existsSync(src) || !existsSync(dirname(dest))) { | ||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| rmSync(dest, { recursive: true, force: true }); | ||||||||||||||||||||||||||||||||||||||||||||||
| cpSync(src, dest, { recursive: true }); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+13
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't let a best-effort postinstall patch fail the install. This helper is called from the published 🔧 Safer postinstall patching function replaceDir(src, dest) {
if (!existsSync(src) || !existsSync(dirname(dest))) {
return;
}
- rmSync(dest, { recursive: true, force: true });
- cpSync(src, dest, { recursive: true });
+ try {
+ rmSync(dest, { recursive: true, force: true });
+ cpSync(src, dest, { recursive: true });
+ } catch (error) {
+ console.warn(
+ `[pp-dev] Skipping bundled npm patch for ${dest}: ${
+ error instanceof Error ? error.message : String(error)
+ }`,
+ );
+ }
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| if (existsSync(npmRoot)) { | ||||||||||||||||||||||||||||||||||||||||||||||
| replaceDir( | ||||||||||||||||||||||||||||||||||||||||||||||
| join(root, "node_modules", "brace-expansion"), | ||||||||||||||||||||||||||||||||||||||||||||||
| join(npmRoot, "node_modules", "brace-expansion"), | ||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||
| replaceDir( | ||||||||||||||||||||||||||||||||||||||||||||||
| join(root, "node_modules", "picomatch"), | ||||||||||||||||||||||||||||||||||||||||||||||
| join(npmRoot, "node_modules", "tinyglobby", "node_modules", "picomatch"), | ||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -160,6 +160,61 @@ interface GlobalCLIOptions { | |||||||||||||||||||||||||||||||||||
| force?: boolean; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** Extra flags for `pp-dev next` (Next.js custom server bundler selection; Next 15+). */ | ||||||||||||||||||||||||||||||||||||
| interface NextCommandCLIOptions extends GlobalCLIOptions { | ||||||||||||||||||||||||||||||||||||
| /** Use Webpack for dev (same idea as `next dev --webpack`). */ | ||||||||||||||||||||||||||||||||||||
| webpack?: boolean; | ||||||||||||||||||||||||||||||||||||
| /** Use Turbopack when native bindings are available. */ | ||||||||||||||||||||||||||||||||||||
| turbopack?: boolean; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| type NextBundlerChoice = { webpack?: boolean; turbopack?: boolean }; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| function parseNextBundlerCli(opts: NextCommandCLIOptions): NextBundlerChoice { | ||||||||||||||||||||||||||||||||||||
| const envWebpack = | ||||||||||||||||||||||||||||||||||||
| process.env.PP_DEV_NEXT_WEBPACK === '1' || | ||||||||||||||||||||||||||||||||||||
| process.env.PP_DEV_NEXT_WEBPACK === 'true'; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (envWebpack && (opts.webpack || opts.turbopack)) { | ||||||||||||||||||||||||||||||||||||
| throw new Error( | ||||||||||||||||||||||||||||||||||||
| 'Do not combine PP_DEV_NEXT_WEBPACK with --webpack or --turbopack', | ||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (envWebpack) { | ||||||||||||||||||||||||||||||||||||
| return { webpack: true }; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (opts.webpack && opts.turbopack) { | ||||||||||||||||||||||||||||||||||||
| throw new Error('Use only one of --webpack or --turbopack'); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (opts.webpack) { | ||||||||||||||||||||||||||||||||||||
| return { webpack: true }; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (opts.turbopack) { | ||||||||||||||||||||||||||||||||||||
| return { turbopack: true }; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| return {}; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** Next dev chose Turbopack but native @next/swc bindings failed (e.g. WDAC on Windows). */ | ||||||||||||||||||||||||||||||||||||
| function isNextTurbopackNativeBindingsError(error: unknown): boolean { | ||||||||||||||||||||||||||||||||||||
| if (!error || typeof error !== 'object' || !('message' in error)) { | ||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const msg = String((error as Error).message); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||
| /Turbopack is not supported/i.test(msg) || | ||||||||||||||||||||||||||||||||||||
| /native bindings are not available/i.test(msg) || | ||||||||||||||||||||||||||||||||||||
| /Only WebAssembly \(WASM\) bindings were loaded/i.test(msg) | ||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| interface ChangelogOptions { | ||||||||||||||||||||||||||||||||||||
| oldAssetsPath?: string; | ||||||||||||||||||||||||||||||||||||
| newAssetsPath?: string; | ||||||||||||||||||||||||||||||||||||
|
|
@@ -503,7 +558,15 @@ cli | |||||||||||||||||||||||||||||||||||
| '--force', | ||||||||||||||||||||||||||||||||||||
| `[boolean] force the optimizer to ignore the cache and re-bundle`, | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| .action(async (root: string, options: ServerOptions & GlobalCLIOptions) => { | ||||||||||||||||||||||||||||||||||||
| .option( | ||||||||||||||||||||||||||||||||||||
| '--webpack', | ||||||||||||||||||||||||||||||||||||
| `[boolean] use Webpack for Next dev (use when Turbopack/native SWC is unavailable)`, | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| .option( | ||||||||||||||||||||||||||||||||||||
| '--turbopack', | ||||||||||||||||||||||||||||||||||||
| `[boolean] use Turbopack for Next dev when native bindings work`, | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| .action(async (root: string, options: ServerOptions & NextCommandCLIOptions) => { | ||||||||||||||||||||||||||||||||||||
| filterDuplicateOptions(options); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| let nextApp: ReturnType<typeof import('next').default> | null = null; | ||||||||||||||||||||||||||||||||||||
|
|
@@ -646,7 +709,7 @@ cli | |||||||||||||||||||||||||||||||||||
| 1; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Get template name from config, package.json, or fallback to project directory name | ||||||||||||||||||||||||||||||||||||
| let templateName = null; | ||||||||||||||||||||||||||||||||||||
| let templateName: string | null = null; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (!templateName) { | ||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||
|
|
@@ -678,19 +741,77 @@ cli | |||||||||||||||||||||||||||||||||||
| base += `/${templateName}`; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| nextApp = next({ | ||||||||||||||||||||||||||||||||||||
| dev: true, | ||||||||||||||||||||||||||||||||||||
| hostname: (opts.host as string) || 'localhost', | ||||||||||||||||||||||||||||||||||||
| port: opts.port, | ||||||||||||||||||||||||||||||||||||
| dir: projectRoot, | ||||||||||||||||||||||||||||||||||||
| conf: { | ||||||||||||||||||||||||||||||||||||
| ...config, | ||||||||||||||||||||||||||||||||||||
| basePath: base, | ||||||||||||||||||||||||||||||||||||
| assetPrefix: `${templateLess ? pathPagePrefix : '/pt'}/${templateName}`, // Fixed: Make assetPrefix consistent with basePath | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
| const bundlerChoice = parseNextBundlerCli(options); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| await nextApp.prepare(); | ||||||||||||||||||||||||||||||||||||
| const createAndPrepareNext = async (bundler: NextBundlerChoice) => { | ||||||||||||||||||||||||||||||||||||
| // Next's createServer() mutates process.env.TURBOPACK. Clear it before each | ||||||||||||||||||||||||||||||||||||
| // attempt so a failed "auto" run does not leave TURBOPACK=auto and block Webpack. | ||||||||||||||||||||||||||||||||||||
| delete process.env.TURBOPACK; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (nextApp && typeof nextApp.close === 'function') { | ||||||||||||||||||||||||||||||||||||
| await nextApp.close(); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| nextApp = null; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const nextOptions: Parameters<typeof next>[0] = { | ||||||||||||||||||||||||||||||||||||
| dev: true, | ||||||||||||||||||||||||||||||||||||
| customServer: true, | ||||||||||||||||||||||||||||||||||||
| hostname: (opts.host as string) || 'localhost', | ||||||||||||||||||||||||||||||||||||
| port: opts.port, | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+757
to
+761
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's check the exact code at lines 757-761
sed -n '750,770p' src/cli.tsRepository: mi-examples/pp-dev Length of output: 661 🏁 Script executed: # Now let's find all usages of opts.host in the file to understand the pattern
rg -n 'opts\.host' src/cli.ts -B 2 -A 2Repository: mi-examples/pp-dev Length of output: 500 🏁 Script executed: # Let's also check the type of opts to understand opts.host
rg -n 'typeof opts\.host' src/cli.ts -B 3 -A 3Repository: mi-examples/pp-dev Length of output: 383 🏁 Script executed: # Find the httpServer.listen call to see how host is used there
rg -n 'httpServer\.listen' src/cli.ts -B 5 -A 5Repository: mi-examples/pp-dev Length of output: 484 🏁 Script executed: # Let's get a broader view of the function containing lines 757-761
sed -n '720,820p' src/cli.tsRepository: mi-examples/pp-dev Length of output: 3229 🏁 Script executed: # Find the type definition or interface for opts
rg -n 'opts.*:' src/cli.ts -B 5 -A 5 | head -100Repository: mi-examples/pp-dev Length of output: 977 🏁 Script executed: # Search for options or interface definitions near the function
rg -n 'interface.*Options|type.*Options' src/cli.tsRepository: mi-examples/pp-dev Length of output: 342 🏁 Script executed: # Look for where opts is defined/destructured in the command definition
rg -n '\(options\)|\(opts\)' src/cli.ts -B 3 -A 3 | head -50Repository: mi-examples/pp-dev Length of output: 1836 🏁 Script executed: # Check yargs/commander configuration for --host flag to understand its type
rg -n '\-\-host|host.*boolean|host.*string' src/cli.ts -B 2 -A 2Repository: mi-examples/pp-dev Length of output: 1478 Normalize The CLI option accepts 🔧 Suggested normalization+ const normalizedHost =
+ opts.host === true
+ ? '0.0.0.0'
+ : typeof opts.host === 'string' && opts.host
+ ? opts.host
+ : 'localhost';
+
const nextOptions: Parameters<typeof next>[0] = {
dev: true,
customServer: true,
- hostname: (opts.host as string) || 'localhost',
+ hostname: normalizedHost,
port: opts.port,
dir: projectRoot,Mirror the same 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
| dir: projectRoot, | ||||||||||||||||||||||||||||||||||||
| conf: { | ||||||||||||||||||||||||||||||||||||
| ...config, | ||||||||||||||||||||||||||||||||||||
| basePath: base, | ||||||||||||||||||||||||||||||||||||
| assetPrefix: `${templateLess ? pathPagePrefix : '/pt'}/${templateName}`, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+763
to
+767
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use the computed base path for
🔧 Proposed fix conf: {
...config,
basePath: base,
- assetPrefix: `${templateLess ? pathPagePrefix : '/pt'}/${templateName}`,
+ assetPrefix: base,
},📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (bundler.webpack) { | ||||||||||||||||||||||||||||||||||||
| nextOptions.webpack = true; | ||||||||||||||||||||||||||||||||||||
| } else if (bundler.turbopack) { | ||||||||||||||||||||||||||||||||||||
| nextOptions.turbopack = true; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| nextApp = next(nextOptions); | ||||||||||||||||||||||||||||||||||||
| await nextApp!.prepare(); | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (bundlerChoice.webpack) { | ||||||||||||||||||||||||||||||||||||
| await createAndPrepareNext({ webpack: true }); | ||||||||||||||||||||||||||||||||||||
| } else if (bundlerChoice.turbopack) { | ||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||
| await createAndPrepareNext({ turbopack: true }); | ||||||||||||||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||||||||||||||
| if (!isNextTurbopackNativeBindingsError(e)) { | ||||||||||||||||||||||||||||||||||||
| throw e; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| logger.warn( | ||||||||||||||||||||||||||||||||||||
| colors.yellow( | ||||||||||||||||||||||||||||||||||||
| '⚠ Turbopack is unavailable (native bindings). Falling back to Webpack.', | ||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||
| await createAndPrepareNext({ webpack: true }); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||
| await createAndPrepareNext({}); | ||||||||||||||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||||||||||||||
| if (!isNextTurbopackNativeBindingsError(e)) { | ||||||||||||||||||||||||||||||||||||
| throw e; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| logger.warn( | ||||||||||||||||||||||||||||||||||||
| colors.yellow( | ||||||||||||||||||||||||||||||||||||
| '⚠ Turbopack cannot run (native Next.js bindings unavailable). Falling back to Webpack.', | ||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||
| await createAndPrepareNext({ webpack: true }); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (!nextApp) { | ||||||||||||||||||||||||||||||||||||
| throw new Error('Next.js app failed to initialize'); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (!base.endsWith('/')) { | ||||||||||||||||||||||||||||||||||||
| base += '/'; | ||||||||||||||||||||||||||||||||||||
|
|
@@ -870,7 +991,10 @@ cli | |||||||||||||||||||||||||||||||||||
| // Note: We need to adapt Express middlewares to work with raw HTTP requests | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // 1. PP Redirect middleware (essential for all routes) | ||||||||||||||||||||||||||||||||||||
| const ppRedirectMiddleware = initPPRedirect(base, templateName); | ||||||||||||||||||||||||||||||||||||
| const ppRedirectMiddleware = initPPRedirect( | ||||||||||||||||||||||||||||||||||||
| base, | ||||||||||||||||||||||||||||||||||||
| templateName ?? undefined, | ||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||
| const ppRedirectWrapper = (req: any, res: any, next: () => void) => { | ||||||||||||||||||||||||||||||||||||
| ppRedirectMiddleware(req, res, next); | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the heading jump and expand the release notes.
The new entry goes from
#to###, which is why markdownlint reports MD001 here. I'd also call out the dependency/toolchain compatibility changes and the packaged security patch in this release so the changelog matches what0.17.0-beta.1actually ships.As per coding guidelines, "Use proper markdown headers (##, ###) for GitHub compatibility in PR messages", "List specific technical improvements in PR messages", and "Specify breaking changes or compatibility in PR messages".
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)
[warning] 4-4: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3
(MD001, heading-increment)
🤖 Prompt for AI Agents