Skip to content
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

[PPR] Dynamic API Debugging #61798

Merged
merged 2 commits into from
Mar 26, 2024
Merged

[PPR] Dynamic API Debugging #61798

merged 2 commits into from
Mar 26, 2024

Conversation

wyattjoh
Copy link
Member

@wyattjoh wyattjoh commented Feb 8, 2024

What?

This adds dynamic usage logging when the __nextppronly query parameter is set and partial prerendering has been enabled. This will print the stack trace for the accesses for all Dynamic API's that were called. This includes those API's that were called during the flight render if they were called before the static shell was ready.

Why?

To take the most advantage of partial prerendering, it's important to track where Dynamic API's are called so developers can determine what has caused part of the component tree not to be included in the static shell. This also helps debug situations where the error thrown by Dynamic API's (used internally for tracking and interruption) are caught but not re-thrown, but due to the implementation of the flight renderer provided by React, we are unable to distinguish between errors that are swallowed by user code or by the flight renderer.

How?

Instead of using a boolean to track if a dynamic API was used, this actually captures the stack at the callsite, allowing developers to find the location in the codebase that invoked it.

Closes NEXT-2399

@ijjk ijjk added created-by: Next.js team PRs by the Next.js team. type: next labels Feb 8, 2024
@wyattjoh
Copy link
Member Author

wyattjoh commented Feb 8, 2024

This stack of pull requests is managed by Graphite. Learn more about stacking.

Join @wyattjoh and the rest of your teammates on Graphite Graphite

@wyattjoh wyattjoh marked this pull request as ready for review February 8, 2024 00:58
@wyattjoh wyattjoh marked this pull request as draft February 8, 2024 00:59
@ijjk
Copy link
Member

ijjk commented Feb 8, 2024

Stats from current PR

Default Build (Increase detected ⚠️)
General Overall increase ⚠️
vercel/next.js canary vercel/next.js feat/ppr-error-stacks Change
buildDuration 14.2s 14.1s N/A
buildDurationCached 8.7s 6.3s N/A
nodeModulesSize 198 MB 198 MB ⚠️ +75.8 kB
nextStartRea..uration (ms) 402ms 410ms N/A
Client Bundles (main, webpack) Overall increase ⚠️
vercel/next.js canary vercel/next.js feat/ppr-error-stacks Change
2453-HASH.js gzip 30.8 kB 31 kB ⚠️ +157 B
3304.HASH.js gzip 181 B 181 B
3f784ff6-HASH.js gzip 53.7 kB 53.7 kB
8299-HASH.js gzip 5.04 kB 5.04 kB N/A
framework-HASH.js gzip 45.2 kB 45.2 kB
main-app-HASH.js gzip 242 B 242 B
main-HASH.js gzip 32.2 kB 32.2 kB N/A
webpack-HASH.js gzip 1.68 kB 1.68 kB N/A
Overall change 130 kB 130 kB ⚠️ +157 B
Legacy Client Bundles (polyfills)
vercel/next.js canary vercel/next.js feat/ppr-error-stacks Change
polyfills-HASH.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary vercel/next.js feat/ppr-error-stacks Change
_app-HASH.js gzip 196 B 197 B N/A
_error-HASH.js gzip 184 B 184 B
amp-HASH.js gzip 505 B 505 B
css-HASH.js gzip 324 B 325 B N/A
dynamic-HASH.js gzip 2.5 kB 2.5 kB N/A
edge-ssr-HASH.js gzip 258 B 258 B
head-HASH.js gzip 352 B 352 B
hooks-HASH.js gzip 370 B 371 B N/A
image-HASH.js gzip 4.21 kB 4.21 kB
index-HASH.js gzip 259 B 259 B
link-HASH.js gzip 2.67 kB 2.67 kB N/A
routerDirect..HASH.js gzip 314 B 312 B N/A
script-HASH.js gzip 386 B 386 B
withRouter-HASH.js gzip 309 B 309 B
1afbb74e6ecf..834.css gzip 106 B 106 B
Overall change 6.57 kB 6.57 kB
Client Build Manifests
vercel/next.js canary vercel/next.js feat/ppr-error-stacks Change
_buildManifest.js gzip 481 B 484 B N/A
Overall change 0 B 0 B
Rendered Page Sizes
vercel/next.js canary vercel/next.js feat/ppr-error-stacks Change
index.html gzip 529 B 529 B
link.html gzip 541 B 541 B
withRouter.html gzip 524 B 523 B N/A
Overall change 1.07 kB 1.07 kB
Edge SSR bundle Size
vercel/next.js canary vercel/next.js feat/ppr-error-stacks Change
edge-ssr.js gzip 95.3 kB 95.3 kB N/A
page.js gzip 3.04 kB 3.04 kB N/A
Overall change 0 B 0 B
Middleware size
vercel/next.js canary vercel/next.js feat/ppr-error-stacks Change
middleware-b..fest.js gzip 624 B 625 B N/A
middleware-r..fest.js gzip 151 B 151 B
middleware.js gzip 25.5 kB 25.5 kB N/A
edge-runtime..pack.js gzip 839 B 839 B
Overall change 990 B 990 B
Next Runtimes Overall increase ⚠️
vercel/next.js canary vercel/next.js feat/ppr-error-stacks Change
app-page-exp...dev.js gzip 170 kB 170 kB ⚠️ +210 B
app-page-exp..prod.js gzip 96.8 kB 97 kB ⚠️ +203 B
app-page-tur..prod.js gzip 98.5 kB 98.7 kB ⚠️ +202 B
app-page-tur..prod.js gzip 92.8 kB 93 kB ⚠️ +201 B
app-page.run...dev.js gzip 144 kB 144 kB ⚠️ +208 B
app-page.run..prod.js gzip 91.3 kB 91.5 kB ⚠️ +202 B
app-route-ex...dev.js gzip 21.3 kB 21.4 kB N/A
app-route-ex..prod.js gzip 15 kB 15.1 kB N/A
app-route-tu..prod.js gzip 15 kB 15.1 kB N/A
app-route-tu..prod.js gzip 14.8 kB 14.8 kB N/A
app-route.ru...dev.js gzip 21 kB 21 kB N/A
app-route.ru..prod.js gzip 14.8 kB 14.8 kB N/A
pages-api-tu..prod.js gzip 9.55 kB 9.55 kB
pages-api.ru...dev.js gzip 9.82 kB 9.82 kB
pages-api.ru..prod.js gzip 9.55 kB 9.55 kB
pages-turbo...prod.js gzip 22.5 kB 22.5 kB
pages.runtim...dev.js gzip 23.1 kB 23.1 kB
pages.runtim..prod.js gzip 22.4 kB 22.4 kB
server.runti..prod.js gzip 50.9 kB 51 kB N/A
Overall change 790 kB 791 kB ⚠️ +1.23 kB
build cache Overall increase ⚠️
vercel/next.js canary vercel/next.js feat/ppr-error-stacks Change
0.pack gzip 1.57 MB 1.57 MB ⚠️ +5.04 kB
index.pack gzip 106 kB 106 kB ⚠️ +459 B
Overall change 1.67 MB 1.68 MB ⚠️ +5.5 kB
Diff details
Diff for middleware.js

Diff too large to display

Diff for edge-ssr.js

Diff too large to display

Diff for 2453-HASH.js

Diff too large to display

Diff for app-page-exp..ntime.dev.js

Diff too large to display

Diff for app-page-exp..time.prod.js

Diff too large to display

Diff for app-page-tur..time.prod.js

Diff too large to display

Diff for app-page-tur..time.prod.js

Diff too large to display

Diff for app-page.runtime.dev.js
failed to diff
Diff for app-page.runtime.prod.js

Diff too large to display

Diff for app-route-ex..ntime.dev.js

Diff too large to display

Diff for app-route-ex..time.prod.js

Diff too large to display

Diff for app-route-tu..time.prod.js

Diff too large to display

Diff for app-route-tu..time.prod.js

Diff too large to display

Diff for app-route.runtime.dev.js

Diff too large to display

Diff for app-route.ru..time.prod.js

Diff too large to display

Diff for server.runtime.prod.js

Diff too large to display

Commit: 1821cc8

Comment on lines 1283 to 1371
for (const stack of stacks) {
error(stack)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems noisy for something that isn't an error case. Imagine you are happy with the level of dynamism of the app, you probably don't want logs on every build or revalidate that remind you of all the places you are triggering dynamic bailouts.

Maybe this is something we write to a manifest instead and there is some tooling you can do to print nicely dynamic usages after the build. And vercel can highlight this as part of the build output if you want to go investigate it after the fact.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't going to log every dynamic bailout, it will only log when there was a dynamic bailout but no postponed state generated, indicating that there was a errant try/catch present and React wasn't able to identify it as a postpone error.

@wyattjoh wyattjoh changed the title feat: more comprehensive stacks for dynamic errors that are captured but not rethrown [PPR] Dynamic API Debugging Mar 21, 2024
Comment on lines +228 to +245
.filter((line) => {
// Exclude Next.js internals from the stack trace.
if (line.includes('node_modules/next/')) {
return false
}

// Exclude anonymous functions from the stack trace.
if (line.includes(' (<anonymous>)')) {
return false
}

// Exclude Node.js internals from the stack trace.
if (line.includes(' (node:')) {
return false
}

return true
})
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This formatting may be controversial, we can remove it if desired.

@wyattjoh wyattjoh marked this pull request as ready for review March 21, 2024 22:26
@wyattjoh wyattjoh requested a review from gnoff March 21, 2024 23:10
Comment on lines 28 to 44
} & Pick<
// Pull some properties from RenderOptsPartial so that the docs are also
// mirrored.
RenderOptsPartial,
| 'originalPathname'
| 'supportsDynamicHTML'
| 'isRevalidate'
| 'isBot'
| 'nextExport'
| 'isDraftMode'
| 'isDebugPPRSkeleton'
>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe something to follow up on later but having to repeat these values just so they can be exposed in async context is probably not good long term. We should maybe just make renderOpts available through a generalization of the staticGenerationContext (it's not really about staticGeneration only anymore anyway)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd argue in favour of narrowing the types here. The biggest problem with RenderOpts is that it's overloaded, and holds way too many fields. Scoping down the fields pulled from here is a small step towards breaking the type up into smaller parts.

/**
* When true, stack information will also be tracked during dynamic access.
*/
isDebugSkeleton: boolean | undefined
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't really a property of the prerenderState but a property of the render itself. this object should generally hold mutable state about the the render and isDebugSkeleton shouldn't be mutated by the render itself so it shouldn't really live here

if (
staticGenerationStore.prerenderState &&
usedDynamicAPIs(staticGenerationStore.prerenderState) &&
staticGenerationStore.prerenderState?.isDebugSkeleton
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused about how this is supposed to work. We only have a prerenderState during build (and revalidate) but we shouldn't have a __nextppronly query param at this time so won't this be false anytime the prerenderState exists?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The __nextppronly query parameter is a stop-gap until we have more comprehensive debugging available for suspense and/or partial prerendering. While running with next dev, when partial prerendering is enabled, you can show the static shell only. This pull request extends this to also print the Dynamic API's used that influenced the static shell generation. Any Dynamic API not called during the static shell generation doesn't affect it (yet).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in Dev the prerenderState exists when the query param is set?

@wyattjoh wyattjoh requested a review from a team as a code owner March 25, 2024 22:01
@wyattjoh wyattjoh requested a review from gnoff March 25, 2024 23:58
@wyattjoh wyattjoh enabled auto-merge (squash) March 26, 2024 17:09
@ijjk
Copy link
Member

ijjk commented Mar 26, 2024

Failing test suites

Commit: 1821cc8

pnpm test test/integration/export-default-map/test/index.test.js

  • Export with default map > production mode > should export with folder that has dot in name
  • Export with default map > production mode > should export an amp only page to clean path
  • Export with default map > production mode > should export hybrid amp page correctly
  • Export with default map > production mode > should export nested hybrid amp page correctly
  • Export with default map > production mode > should export nested hybrid amp page correctly with folder
  • Export with default map > production mode > should export hybrid index amp page correctly
Expand output

● Export with default map › production mode › should export with folder that has dot in name

command failed with code 1 signal null
  ▲ Next.js 14.2.0-canary.43

   Linting and checking validity of types ...

 ⚠ The Next.js plugin was not detected in your ESLint configuration. See https://nextjs.org/docs/basic-features/eslint#migrating-existing-config
   Creating an optimized production build ...
 ✓ Compiled successfully
   Collecting page data ...
   Generating static pages (0/9) ...
   Generating static pages (2/9) 
   Generating static pages (4/9) 

Error occurred prerendering page "/info". Read more: https://nextjs.org/docs/messages/prerender-error

Error: Could not read from https://cdn.ampproject.org/v0/validator_wasm.js - aborted

  15 |  * See the License for the specific language governing permissions and
  16 |  * limitations under the license.
> 17 |  */const t=__nccwpck_require__(915);const r=__nccwpck_require__(147);const i=__nccwpck_require__(685);const s=__nccwpck_require__(687);const o=__nccwpck_require__(17);const a=__nccwpck_require__(642);const l=__nccwpck_require__(263);const u=__nccwpck_require__(477);const c=__nccwpck_require__(310);const h=__nccwpck_require__(837);const p=__nccwpck_require__(144);const f="amphtml-validator";function hasPrefix(e,t){return e.indexOf(t)==0}function isHttpOrHttpsUrl(e){return hasPrefix(e,"http://")||hasPrefix(e,"https://")}function readFromFile(e){return new l((function(t,n){r.readFile(e,"utf8",(function(e,r){if(e){n(e)}else{t(r.trim())}}))}))}function readFromReadable(e,t){return new l((function(n,r){const i=[];t.setEncoding("utf8");t.on("data",(function(e){i.push(e)}));t.on("end",(function(){n(i.join(""))}));t.on("error",(function(t){r(new Error("Could not read from "+e+" - "+t.message))}))}))}function readFromStdin(){return readFromReadable("stdin",process.stdin).then((function(e){process.stdin.resume();return e}))}function readFromUrl(e,t){return new l((function(n,r){const o=hasPrefix(e,"http://")?i:s;const a=o.request(e,(function(t){if(t.statusCode!==200){t.resume();r(new Error("Unable to fetch "+e+" - HTTP Status "+t.statusCode))}else{n(t)}}));a.setHeader("User-Agent",t);a.on("error",(function(t){r(new Error("Unable to fetch "+e+" - "+t.message))}));a.end()})).then(readFromReadable.bind(null,e))}function ValidationResult(){this.status="UNKNOWN";this.errors=[]}function ValidationError(){this.severity="UNKNOWN_SEVERITY";this.line=1;this.col=0;this.message="";this.specUrl=null;this.code="UNKNOWN_CODE";this.params=[]}function Validator(e){this.sandbox=p.createContext();try{new p.Script(e).runInContext(this.sandbox)}catch(e){throw new Error("Could not instantiate validator_wasm.js - "+e.message)}}Validator.prototype.init=function(){if(this.sandbox.amp.validator.init){return this.sandbox.amp.validator.init()}else{return l.resolve(undefined)}};Validator.prototype.validateString=function(e,t){const n=this.sandbox.amp.validator.validateString(e,t);const r=new ValidationResult;r.status=n.status;for(let e=0;e<n.errors.length;e++){const t=n.errors[e];const i=new ValidationError;i.severity=t.severity;i.line=t.line;i.col=t.col;i.message=this.sandbox.amp.validator.renderErrorMessage(t);i.specUrl=t.specUrl;i.code=t.code;i.params=t.params;r.errors.push(i)}return r};const m={};function getInstance(e,t){const n=e||"https://cdn.ampproject.org/v0/validator_wasm.js";const r=t||f;if(m.hasOwnProperty(n)){return l.resolve(m[n])}const i=isHttpOrHttpsUrl(n)?readFromUrl(n,r):readFromFile(n);return i.then((function(e){let t;try{t=new Validator(e)}catch(e){throw e}m[n]=t;return t})).then((function(e){return e.init().then((()=>e))}))}e.getInstance=getInstance;function newInstance(e){return new Validator(e)}e.newInstance=newInstance;function logValidationResult(e,n,r){if(n.status==="PASS"){process.stdout.write(e+": "+(r?t.green("PASS"):"PASS")+"\n")}for(let i=0;i<n.errors.length;i++){const s=n.errors[i];let o=e+":"+s.line+":"+s.col+" ";if(r){o+=(s.severity==="ERROR"?t.red:t.magenta)(s.message)}else{o+=s.message}if(s.specUrl){o+=" (see "+s.specUrl+")"}process.stderr.write(o+"\n")}}function main(){a.usage("[options] <fileOrUrlOrMinus...>\n\n"+'  Validates the files or urls provided as arguments. If "-" is\n'+"  specified, reads from stdin instead.").option("--validator_js <fileOrUrl>","The Validator Javascript.\n"+"  Latest published version by default, or\n"+"  dist/validator_minified.js (built with build.py)\n"+"  for development.","https://cdn.ampproject.org/v0/validator_wasm.js").option("--user-agent <userAgent>","User agent string to use in requests.",f).option("--html_format <AMP|AMP4ADS|AMP4EMAIL>","The input format to be validated.\n"+"  AMP by default.","AMP").option("--format <color|text|json>","How to format the output.\n"+'  "color" displays errors/warnings/success in\n'+"          red/orange/green.\n"+'  "text"  avoids color (e.g., useful in terminals not\n'+"          supporting color).\n"+'  "json"  emits json corresponding to the ValidationResult\n'+"          message in validator.proto.","color").parse(process.argv);const e=a.opts();if(e.length===0){a.outputHelp();process.exit(1)}if(e.html_format!=="AMP"&&e.html_format!=="AMP4ADS"&&e.html_format!=="AMP4EMAIL"){process.stderr.write('--html_format must be set to "AMP", "AMP4ADS", or "AMP4EMAIL".\n',(function(){process.exit(1)}))}if(e.format!=="color"&&e.format!=="text"&&e.format!=="json"){process.stderr.write('--format must be set to "color", "text", or "json".\n',(function(){process.exit(1)}))}const n=[];for(let t=0;t<a.args.length;t++){const r=a.args[t];if(r==="-"){n.push(readFromStdin())}else if(isHttpOrHttpsUrl(r)){n.push(readFromUrl(r,e.userAgent))}else{n.push(readFromFile(r))}}getInstance(e.validator_js,e.userAgent).then((function(r){l.all(n).then((function(t){const n={};let i=false;for(let s=0;s<t.length;s++){const o=r.validateString(t[s],e.html_format);if(e.format==="json"){n[a.args[s]]=o}else{logValidationResult(a.args[s],o,e.format==="color"?true:false)}if(o.status!=="PASS"){i=true}}if(e.format==="json"){process.stdout.write(JSON.stringify(n)+"\n",(function(){process.exit(i?1:0)}))}else if(i){process.stderr.write("",(function(){process.exit(1)}))}else{process.stdout.write("",(function(){process.exit(0)}))}})).catch((function(n){process.stderr.write((e.format=="color"?t.red(n.message):n.message)+"\n",(function(){process.exit(1)}))}))})).catch((function(n){process.stderr.write((e.format=="color"?t.red(n.message):n.message)+"\n",(function(){process.exit(1)}))}))}e.main=main})();module.exports=n})();
     |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              ^

  at IncomingMessage.<anonymous> (../packages/next/dist/compiled/amphtml-validator/index.js:17:846)
     Generating static pages (6/9) 
   ✓ Generating static pages (9/9)
  > Export encountered errors on following paths:
  	/info
  at ChildProcess.<anonymous> (lib/next-test-utils.ts:284:11)

● Export with default map › production mode › should export an amp only page to clean path

command failed with code 1 signal null
  ▲ Next.js 14.2.0-canary.43

   Linting and checking validity of types ...

 ⚠ The Next.js plugin was not detected in your ESLint configuration. See https://nextjs.org/docs/basic-features/eslint#migrating-existing-config
   Creating an optimized production build ...
 ✓ Compiled successfully
   Collecting page data ...
   Generating static pages (0/9) ...
   Generating static pages (2/9) 
   Generating static pages (4/9) 

Error occurred prerendering page "/info". Read more: https://nextjs.org/docs/messages/prerender-error

Error: Could not read from https://cdn.ampproject.org/v0/validator_wasm.js - aborted

  15 |  * See the License for the specific language governing permissions and
  16 |  * limitations under the license.
> 17 |  */const t=__nccwpck_require__(915);const r=__nccwpck_require__(147);const i=__nccwpck_require__(685);const s=__nccwpck_require__(687);const o=__nccwpck_require__(17);const a=__nccwpck_require__(642);const l=__nccwpck_require__(263);const u=__nccwpck_require__(477);const c=__nccwpck_require__(310);const h=__nccwpck_require__(837);const p=__nccwpck_require__(144);const f="amphtml-validator";function hasPrefix(e,t){return e.indexOf(t)==0}function isHttpOrHttpsUrl(e){return hasPrefix(e,"http://")||hasPrefix(e,"https://")}function readFromFile(e){return new l((function(t,n){r.readFile(e,"utf8",(function(e,r){if(e){n(e)}else{t(r.trim())}}))}))}function readFromReadable(e,t){return new l((function(n,r){const i=[];t.setEncoding("utf8");t.on("data",(function(e){i.push(e)}));t.on("end",(function(){n(i.join(""))}));t.on("error",(function(t){r(new Error("Could not read from "+e+" - "+t.message))}))}))}function readFromStdin(){return readFromReadable("stdin",process.stdin).then((function(e){process.stdin.resume();return e}))}function readFromUrl(e,t){return new l((function(n,r){const o=hasPrefix(e,"http://")?i:s;const a=o.request(e,(function(t){if(t.statusCode!==200){t.resume();r(new Error("Unable to fetch "+e+" - HTTP Status "+t.statusCode))}else{n(t)}}));a.setHeader("User-Agent",t);a.on("error",(function(t){r(new Error("Unable to fetch "+e+" - "+t.message))}));a.end()})).then(readFromReadable.bind(null,e))}function ValidationResult(){this.status="UNKNOWN";this.errors=[]}function ValidationError(){this.severity="UNKNOWN_SEVERITY";this.line=1;this.col=0;this.message="";this.specUrl=null;this.code="UNKNOWN_CODE";this.params=[]}function Validator(e){this.sandbox=p.createContext();try{new p.Script(e).runInContext(this.sandbox)}catch(e){throw new Error("Could not instantiate validator_wasm.js - "+e.message)}}Validator.prototype.init=function(){if(this.sandbox.amp.validator.init){return this.sandbox.amp.validator.init()}else{return l.resolve(undefined)}};Validator.prototype.validateString=function(e,t){const n=this.sandbox.amp.validator.validateString(e,t);const r=new ValidationResult;r.status=n.status;for(let e=0;e<n.errors.length;e++){const t=n.errors[e];const i=new ValidationError;i.severity=t.severity;i.line=t.line;i.col=t.col;i.message=this.sandbox.amp.validator.renderErrorMessage(t);i.specUrl=t.specUrl;i.code=t.code;i.params=t.params;r.errors.push(i)}return r};const m={};function getInstance(e,t){const n=e||"https://cdn.ampproject.org/v0/validator_wasm.js";const r=t||f;if(m.hasOwnProperty(n)){return l.resolve(m[n])}const i=isHttpOrHttpsUrl(n)?readFromUrl(n,r):readFromFile(n);return i.then((function(e){let t;try{t=new Validator(e)}catch(e){throw e}m[n]=t;return t})).then((function(e){return e.init().then((()=>e))}))}e.getInstance=getInstance;function newInstance(e){return new Validator(e)}e.newInstance=newInstance;function logValidationResult(e,n,r){if(n.status==="PASS"){process.stdout.write(e+": "+(r?t.green("PASS"):"PASS")+"\n")}for(let i=0;i<n.errors.length;i++){const s=n.errors[i];let o=e+":"+s.line+":"+s.col+" ";if(r){o+=(s.severity==="ERROR"?t.red:t.magenta)(s.message)}else{o+=s.message}if(s.specUrl){o+=" (see "+s.specUrl+")"}process.stderr.write(o+"\n")}}function main(){a.usage("[options] <fileOrUrlOrMinus...>\n\n"+'  Validates the files or urls provided as arguments. If "-" is\n'+"  specified, reads from stdin instead.").option("--validator_js <fileOrUrl>","The Validator Javascript.\n"+"  Latest published version by default, or\n"+"  dist/validator_minified.js (built with build.py)\n"+"  for development.","https://cdn.ampproject.org/v0/validator_wasm.js").option("--user-agent <userAgent>","User agent string to use in requests.",f).option("--html_format <AMP|AMP4ADS|AMP4EMAIL>","The input format to be validated.\n"+"  AMP by default.","AMP").option("--format <color|text|json>","How to format the output.\n"+'  "color" displays errors/warnings/success in\n'+"          red/orange/green.\n"+'  "text"  avoids color (e.g., useful in terminals not\n'+"          supporting color).\n"+'  "json"  emits json corresponding to the ValidationResult\n'+"          message in validator.proto.","color").parse(process.argv);const e=a.opts();if(e.length===0){a.outputHelp();process.exit(1)}if(e.html_format!=="AMP"&&e.html_format!=="AMP4ADS"&&e.html_format!=="AMP4EMAIL"){process.stderr.write('--html_format must be set to "AMP", "AMP4ADS", or "AMP4EMAIL".\n',(function(){process.exit(1)}))}if(e.format!=="color"&&e.format!=="text"&&e.format!=="json"){process.stderr.write('--format must be set to "color", "text", or "json".\n',(function(){process.exit(1)}))}const n=[];for(let t=0;t<a.args.length;t++){const r=a.args[t];if(r==="-"){n.push(readFromStdin())}else if(isHttpOrHttpsUrl(r)){n.push(readFromUrl(r,e.userAgent))}else{n.push(readFromFile(r))}}getInstance(e.validator_js,e.userAgent).then((function(r){l.all(n).then((function(t){const n={};let i=false;for(let s=0;s<t.length;s++){const o=r.validateString(t[s],e.html_format);if(e.format==="json"){n[a.args[s]]=o}else{logValidationResult(a.args[s],o,e.format==="color"?true:false)}if(o.status!=="PASS"){i=true}}if(e.format==="json"){process.stdout.write(JSON.stringify(n)+"\n",(function(){process.exit(i?1:0)}))}else if(i){process.stderr.write("",(function(){process.exit(1)}))}else{process.stdout.write("",(function(){process.exit(0)}))}})).catch((function(n){process.stderr.write((e.format=="color"?t.red(n.message):n.message)+"\n",(function(){process.exit(1)}))}))})).catch((function(n){process.stderr.write((e.format=="color"?t.red(n.message):n.message)+"\n",(function(){process.exit(1)}))}))}e.main=main})();module.exports=n})();
     |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              ^

  at IncomingMessage.<anonymous> (../packages/next/dist/compiled/amphtml-validator/index.js:17:846)
     Generating static pages (6/9) 
   ✓ Generating static pages (9/9)
  > Export encountered errors on following paths:
  	/info
  at ChildProcess.<anonymous> (lib/next-test-utils.ts:284:11)

● Export with default map › production mode › should export hybrid amp page correctly

command failed with code 1 signal null
  ▲ Next.js 14.2.0-canary.43

   Linting and checking validity of types ...

 ⚠ The Next.js plugin was not detected in your ESLint configuration. See https://nextjs.org/docs/basic-features/eslint#migrating-existing-config
   Creating an optimized production build ...
 ✓ Compiled successfully
   Collecting page data ...
   Generating static pages (0/9) ...
   Generating static pages (2/9) 
   Generating static pages (4/9) 

Error occurred prerendering page "/info". Read more: https://nextjs.org/docs/messages/prerender-error

Error: Could not read from https://cdn.ampproject.org/v0/validator_wasm.js - aborted

  15 |  * See the License for the specific language governing permissions and
  16 |  * limitations under the license.
> 17 |  */const t=__nccwpck_require__(915);const r=__nccwpck_require__(147);const i=__nccwpck_require__(685);const s=__nccwpck_require__(687);const o=__nccwpck_require__(17);const a=__nccwpck_require__(642);const l=__nccwpck_require__(263);const u=__nccwpck_require__(477);const c=__nccwpck_require__(310);const h=__nccwpck_require__(837);const p=__nccwpck_require__(144);const f="amphtml-validator";function hasPrefix(e,t){return e.indexOf(t)==0}function isHttpOrHttpsUrl(e){return hasPrefix(e,"http://")||hasPrefix(e,"https://")}function readFromFile(e){return new l((function(t,n){r.readFile(e,"utf8",(function(e,r){if(e){n(e)}else{t(r.trim())}}))}))}function readFromReadable(e,t){return new l((function(n,r){const i=[];t.setEncoding("utf8");t.on("data",(function(e){i.push(e)}));t.on("end",(function(){n(i.join(""))}));t.on("error",(function(t){r(new Error("Could not read from "+e+" - "+t.message))}))}))}function readFromStdin(){return readFromReadable("stdin",process.stdin).then((function(e){process.stdin.resume();return e}))}function readFromUrl(e,t){return new l((function(n,r){const o=hasPrefix(e,"http://")?i:s;const a=o.request(e,(function(t){if(t.statusCode!==200){t.resume();r(new Error("Unable to fetch "+e+" - HTTP Status "+t.statusCode))}else{n(t)}}));a.setHeader("User-Agent",t);a.on("error",(function(t){r(new Error("Unable to fetch "+e+" - "+t.message))}));a.end()})).then(readFromReadable.bind(null,e))}function ValidationResult(){this.status="UNKNOWN";this.errors=[]}function ValidationError(){this.severity="UNKNOWN_SEVERITY";this.line=1;this.col=0;this.message="";this.specUrl=null;this.code="UNKNOWN_CODE";this.params=[]}function Validator(e){this.sandbox=p.createContext();try{new p.Script(e).runInContext(this.sandbox)}catch(e){throw new Error("Could not instantiate validator_wasm.js - "+e.message)}}Validator.prototype.init=function(){if(this.sandbox.amp.validator.init){return this.sandbox.amp.validator.init()}else{return l.resolve(undefined)}};Validator.prototype.validateString=function(e,t){const n=this.sandbox.amp.validator.validateString(e,t);const r=new ValidationResult;r.status=n.status;for(let e=0;e<n.errors.length;e++){const t=n.errors[e];const i=new ValidationError;i.severity=t.severity;i.line=t.line;i.col=t.col;i.message=this.sandbox.amp.validator.renderErrorMessage(t);i.specUrl=t.specUrl;i.code=t.code;i.params=t.params;r.errors.push(i)}return r};const m={};function getInstance(e,t){const n=e||"https://cdn.ampproject.org/v0/validator_wasm.js";const r=t||f;if(m.hasOwnProperty(n)){return l.resolve(m[n])}const i=isHttpOrHttpsUrl(n)?readFromUrl(n,r):readFromFile(n);return i.then((function(e){let t;try{t=new Validator(e)}catch(e){throw e}m[n]=t;return t})).then((function(e){return e.init().then((()=>e))}))}e.getInstance=getInstance;function newInstance(e){return new Validator(e)}e.newInstance=newInstance;function logValidationResult(e,n,r){if(n.status==="PASS"){process.stdout.write(e+": "+(r?t.green("PASS"):"PASS")+"\n")}for(let i=0;i<n.errors.length;i++){const s=n.errors[i];let o=e+":"+s.line+":"+s.col+" ";if(r){o+=(s.severity==="ERROR"?t.red:t.magenta)(s.message)}else{o+=s.message}if(s.specUrl){o+=" (see "+s.specUrl+")"}process.stderr.write(o+"\n")}}function main(){a.usage("[options] <fileOrUrlOrMinus...>\n\n"+'  Validates the files or urls provided as arguments. If "-" is\n'+"  specified, reads from stdin instead.").option("--validator_js <fileOrUrl>","The Validator Javascript.\n"+"  Latest published version by default, or\n"+"  dist/validator_minified.js (built with build.py)\n"+"  for development.","https://cdn.ampproject.org/v0/validator_wasm.js").option("--user-agent <userAgent>","User agent string to use in requests.",f).option("--html_format <AMP|AMP4ADS|AMP4EMAIL>","The input format to be validated.\n"+"  AMP by default.","AMP").option("--format <color|text|json>","How to format the output.\n"+'  "color" displays errors/warnings/success in\n'+"          red/orange/green.\n"+'  "text"  avoids color (e.g., useful in terminals not\n'+"          supporting color).\n"+'  "json"  emits json corresponding to the ValidationResult\n'+"          message in validator.proto.","color").parse(process.argv);const e=a.opts();if(e.length===0){a.outputHelp();process.exit(1)}if(e.html_format!=="AMP"&&e.html_format!=="AMP4ADS"&&e.html_format!=="AMP4EMAIL"){process.stderr.write('--html_format must be set to "AMP", "AMP4ADS", or "AMP4EMAIL".\n',(function(){process.exit(1)}))}if(e.format!=="color"&&e.format!=="text"&&e.format!=="json"){process.stderr.write('--format must be set to "color", "text", or "json".\n',(function(){process.exit(1)}))}const n=[];for(let t=0;t<a.args.length;t++){const r=a.args[t];if(r==="-"){n.push(readFromStdin())}else if(isHttpOrHttpsUrl(r)){n.push(readFromUrl(r,e.userAgent))}else{n.push(readFromFile(r))}}getInstance(e.validator_js,e.userAgent).then((function(r){l.all(n).then((function(t){const n={};let i=false;for(let s=0;s<t.length;s++){const o=r.validateString(t[s],e.html_format);if(e.format==="json"){n[a.args[s]]=o}else{logValidationResult(a.args[s],o,e.format==="color"?true:false)}if(o.status!=="PASS"){i=true}}if(e.format==="json"){process.stdout.write(JSON.stringify(n)+"\n",(function(){process.exit(i?1:0)}))}else if(i){process.stderr.write("",(function(){process.exit(1)}))}else{process.stdout.write("",(function(){process.exit(0)}))}})).catch((function(n){process.stderr.write((e.format=="color"?t.red(n.message):n.message)+"\n",(function(){process.exit(1)}))}))})).catch((function(n){process.stderr.write((e.format=="color"?t.red(n.message):n.message)+"\n",(function(){process.exit(1)}))}))}e.main=main})();module.exports=n})();
     |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              ^

  at IncomingMessage.<anonymous> (../packages/next/dist/compiled/amphtml-validator/index.js:17:846)
     Generating static pages (6/9) 
   ✓ Generating static pages (9/9)
  > Export encountered errors on following paths:
  	/info
  at ChildProcess.<anonymous> (lib/next-test-utils.ts:284:11)

● Export with default map › production mode › should export nested hybrid amp page correctly

command failed with code 1 signal null
  ▲ Next.js 14.2.0-canary.43

   Linting and checking validity of types ...

 ⚠ The Next.js plugin was not detected in your ESLint configuration. See https://nextjs.org/docs/basic-features/eslint#migrating-existing-config
   Creating an optimized production build ...
 ✓ Compiled successfully
   Collecting page data ...
   Generating static pages (0/9) ...
   Generating static pages (2/9) 
   Generating static pages (4/9) 

Error occurred prerendering page "/info". Read more: https://nextjs.org/docs/messages/prerender-error

Error: Could not read from https://cdn.ampproject.org/v0/validator_wasm.js - aborted

  15 |  * See the License for the specific language governing permissions and
  16 |  * limitations under the license.
> 17 |  */const t=__nccwpck_require__(915);const r=__nccwpck_require__(147);const i=__nccwpck_require__(685);const s=__nccwpck_require__(687);const o=__nccwpck_require__(17);const a=__nccwpck_require__(642);const l=__nccwpck_require__(263);const u=__nccwpck_require__(477);const c=__nccwpck_require__(310);const h=__nccwpck_require__(837);const p=__nccwpck_require__(144);const f="amphtml-validator";function hasPrefix(e,t){return e.indexOf(t)==0}function isHttpOrHttpsUrl(e){return hasPrefix(e,"http://")||hasPrefix(e,"https://")}function readFromFile(e){return new l((function(t,n){r.readFile(e,"utf8",(function(e,r){if(e){n(e)}else{t(r.trim())}}))}))}function readFromReadable(e,t){return new l((function(n,r){const i=[];t.setEncoding("utf8");t.on("data",(function(e){i.push(e)}));t.on("end",(function(){n(i.join(""))}));t.on("error",(function(t){r(new Error("Could not read from "+e+" - "+t.message))}))}))}function readFromStdin(){return readFromReadable("stdin",process.stdin).then((function(e){process.stdin.resume();return e}))}function readFromUrl(e,t){return new l((function(n,r){const o=hasPrefix(e,"http://")?i:s;const a=o.request(e,(function(t){if(t.statusCode!==200){t.resume();r(new Error("Unable to fetch "+e+" - HTTP Status "+t.statusCode))}else{n(t)}}));a.setHeader("User-Agent",t);a.on("error",(function(t){r(new Error("Unable to fetch "+e+" - "+t.message))}));a.end()})).then(readFromReadable.bind(null,e))}function ValidationResult(){this.status="UNKNOWN";this.errors=[]}function ValidationError(){this.severity="UNKNOWN_SEVERITY";this.line=1;this.col=0;this.message="";this.specUrl=null;this.code="UNKNOWN_CODE";this.params=[]}function Validator(e){this.sandbox=p.createContext();try{new p.Script(e).runInContext(this.sandbox)}catch(e){throw new Error("Could not instantiate validator_wasm.js - "+e.message)}}Validator.prototype.init=function(){if(this.sandbox.amp.validator.init){return this.sandbox.amp.validator.init()}else{return l.resolve(undefined)}};Validator.prototype.validateString=function(e,t){const n=this.sandbox.amp.validator.validateString(e,t);const r=new ValidationResult;r.status=n.status;for(let e=0;e<n.errors.length;e++){const t=n.errors[e];const i=new ValidationError;i.severity=t.severity;i.line=t.line;i.col=t.col;i.message=this.sandbox.amp.validator.renderErrorMessage(t);i.specUrl=t.specUrl;i.code=t.code;i.params=t.params;r.errors.push(i)}return r};const m={};function getInstance(e,t){const n=e||"https://cdn.ampproject.org/v0/validator_wasm.js";const r=t||f;if(m.hasOwnProperty(n)){return l.resolve(m[n])}const i=isHttpOrHttpsUrl(n)?readFromUrl(n,r):readFromFile(n);return i.then((function(e){let t;try{t=new Validator(e)}catch(e){throw e}m[n]=t;return t})).then((function(e){return e.init().then((()=>e))}))}e.getInstance=getInstance;function newInstance(e){return new Validator(e)}e.newInstance=newInstance;function logValidationResult(e,n,r){if(n.status==="PASS"){process.stdout.write(e+": "+(r?t.green("PASS"):"PASS")+"\n")}for(let i=0;i<n.errors.length;i++){const s=n.errors[i];let o=e+":"+s.line+":"+s.col+" ";if(r){o+=(s.severity==="ERROR"?t.red:t.magenta)(s.message)}else{o+=s.message}if(s.specUrl){o+=" (see "+s.specUrl+")"}process.stderr.write(o+"\n")}}function main(){a.usage("[options] <fileOrUrlOrMinus...>\n\n"+'  Validates the files or urls provided as arguments. If "-" is\n'+"  specified, reads from stdin instead.").option("--validator_js <fileOrUrl>","The Validator Javascript.\n"+"  Latest published version by default, or\n"+"  dist/validator_minified.js (built with build.py)\n"+"  for development.","https://cdn.ampproject.org/v0/validator_wasm.js").option("--user-agent <userAgent>","User agent string to use in requests.",f).option("--html_format <AMP|AMP4ADS|AMP4EMAIL>","The input format to be validated.\n"+"  AMP by default.","AMP").option("--format <color|text|json>","How to format the output.\n"+'  "color" displays errors/warnings/success in\n'+"          red/orange/green.\n"+'  "text"  avoids color (e.g., useful in terminals not\n'+"          supporting color).\n"+'  "json"  emits json corresponding to the ValidationResult\n'+"          message in validator.proto.","color").parse(process.argv);const e=a.opts();if(e.length===0){a.outputHelp();process.exit(1)}if(e.html_format!=="AMP"&&e.html_format!=="AMP4ADS"&&e.html_format!=="AMP4EMAIL"){process.stderr.write('--html_format must be set to "AMP", "AMP4ADS", or "AMP4EMAIL".\n',(function(){process.exit(1)}))}if(e.format!=="color"&&e.format!=="text"&&e.format!=="json"){process.stderr.write('--format must be set to "color", "text", or "json".\n',(function(){process.exit(1)}))}const n=[];for(let t=0;t<a.args.length;t++){const r=a.args[t];if(r==="-"){n.push(readFromStdin())}else if(isHttpOrHttpsUrl(r)){n.push(readFromUrl(r,e.userAgent))}else{n.push(readFromFile(r))}}getInstance(e.validator_js,e.userAgent).then((function(r){l.all(n).then((function(t){const n={};let i=false;for(let s=0;s<t.length;s++){const o=r.validateString(t[s],e.html_format);if(e.format==="json"){n[a.args[s]]=o}else{logValidationResult(a.args[s],o,e.format==="color"?true:false)}if(o.status!=="PASS"){i=true}}if(e.format==="json"){process.stdout.write(JSON.stringify(n)+"\n",(function(){process.exit(i?1:0)}))}else if(i){process.stderr.write("",(function(){process.exit(1)}))}else{process.stdout.write("",(function(){process.exit(0)}))}})).catch((function(n){process.stderr.write((e.format=="color"?t.red(n.message):n.message)+"\n",(function(){process.exit(1)}))}))})).catch((function(n){process.stderr.write((e.format=="color"?t.red(n.message):n.message)+"\n",(function(){process.exit(1)}))}))}e.main=main})();module.exports=n})();
     |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              ^

  at IncomingMessage.<anonymous> (../packages/next/dist/compiled/amphtml-validator/index.js:17:846)
     Generating static pages (6/9) 
   ✓ Generating static pages (9/9)
  > Export encountered errors on following paths:
  	/info
  at ChildProcess.<anonymous> (lib/next-test-utils.ts:284:11)

● Export with default map › production mode › should export nested hybrid amp page correctly with folder

command failed with code 1 signal null
  ▲ Next.js 14.2.0-canary.43

   Linting and checking validity of types ...

 ⚠ The Next.js plugin was not detected in your ESLint configuration. See https://nextjs.org/docs/basic-features/eslint#migrating-existing-config
   Creating an optimized production build ...
 ✓ Compiled successfully
   Collecting page data ...
   Generating static pages (0/9) ...
   Generating static pages (2/9) 
   Generating static pages (4/9) 

Error occurred prerendering page "/info". Read more: https://nextjs.org/docs/messages/prerender-error

Error: Could not read from https://cdn.ampproject.org/v0/validator_wasm.js - aborted

  15 |  * See the License for the specific language governing permissions and
  16 |  * limitations under the license.
> 17 |  */const t=__nccwpck_require__(915);const r=__nccwpck_require__(147);const i=__nccwpck_require__(685);const s=__nccwpck_require__(687);const o=__nccwpck_require__(17);const a=__nccwpck_require__(642);const l=__nccwpck_require__(263);const u=__nccwpck_require__(477);const c=__nccwpck_require__(310);const h=__nccwpck_require__(837);const p=__nccwpck_require__(144);const f="amphtml-validator";function hasPrefix(e,t){return e.indexOf(t)==0}function isHttpOrHttpsUrl(e){return hasPrefix(e,"http://")||hasPrefix(e,"https://")}function readFromFile(e){return new l((function(t,n){r.readFile(e,"utf8",(function(e,r){if(e){n(e)}else{t(r.trim())}}))}))}function readFromReadable(e,t){return new l((function(n,r){const i=[];t.setEncoding("utf8");t.on("data",(function(e){i.push(e)}));t.on("end",(function(){n(i.join(""))}));t.on("error",(function(t){r(new Error("Could not read from "+e+" - "+t.message))}))}))}function readFromStdin(){return readFromReadable("stdin",process.stdin).then((function(e){process.stdin.resume();return e}))}function readFromUrl(e,t){return new l((function(n,r){const o=hasPrefix(e,"http://")?i:s;const a=o.request(e,(function(t){if(t.statusCode!==200){t.resume();r(new Error("Unable to fetch "+e+" - HTTP Status "+t.statusCode))}else{n(t)}}));a.setHeader("User-Agent",t);a.on("error",(function(t){r(new Error("Unable to fetch "+e+" - "+t.message))}));a.end()})).then(readFromReadable.bind(null,e))}function ValidationResult(){this.status="UNKNOWN";this.errors=[]}function ValidationError(){this.severity="UNKNOWN_SEVERITY";this.line=1;this.col=0;this.message="";this.specUrl=null;this.code="UNKNOWN_CODE";this.params=[]}function Validator(e){this.sandbox=p.createContext();try{new p.Script(e).runInContext(this.sandbox)}catch(e){throw new Error("Could not instantiate validator_wasm.js - "+e.message)}}Validator.prototype.init=function(){if(this.sandbox.amp.validator.init){return this.sandbox.amp.validator.init()}else{return l.resolve(undefined)}};Validator.prototype.validateString=function(e,t){const n=this.sandbox.amp.validator.validateString(e,t);const r=new ValidationResult;r.status=n.status;for(let e=0;e<n.errors.length;e++){const t=n.errors[e];const i=new ValidationError;i.severity=t.severity;i.line=t.line;i.col=t.col;i.message=this.sandbox.amp.validator.renderErrorMessage(t);i.specUrl=t.specUrl;i.code=t.code;i.params=t.params;r.errors.push(i)}return r};const m={};function getInstance(e,t){const n=e||"https://cdn.ampproject.org/v0/validator_wasm.js";const r=t||f;if(m.hasOwnProperty(n)){return l.resolve(m[n])}const i=isHttpOrHttpsUrl(n)?readFromUrl(n,r):readFromFile(n);return i.then((function(e){let t;try{t=new Validator(e)}catch(e){throw e}m[n]=t;return t})).then((function(e){return e.init().then((()=>e))}))}e.getInstance=getInstance;function newInstance(e){return new Validator(e)}e.newInstance=newInstance;function logValidationResult(e,n,r){if(n.status==="PASS"){process.stdout.write(e+": "+(r?t.green("PASS"):"PASS")+"\n")}for(let i=0;i<n.errors.length;i++){const s=n.errors[i];let o=e+":"+s.line+":"+s.col+" ";if(r){o+=(s.severity==="ERROR"?t.red:t.magenta)(s.message)}else{o+=s.message}if(s.specUrl){o+=" (see "+s.specUrl+")"}process.stderr.write(o+"\n")}}function main(){a.usage("[options] <fileOrUrlOrMinus...>\n\n"+'  Validates the files or urls provided as arguments. If "-" is\n'+"  specified, reads from stdin instead.").option("--validator_js <fileOrUrl>","The Validator Javascript.\n"+"  Latest published version by default, or\n"+"  dist/validator_minified.js (built with build.py)\n"+"  for development.","https://cdn.ampproject.org/v0/validator_wasm.js").option("--user-agent <userAgent>","User agent string to use in requests.",f).option("--html_format <AMP|AMP4ADS|AMP4EMAIL>","The input format to be validated.\n"+"  AMP by default.","AMP").option("--format <color|text|json>","How to format the output.\n"+'  "color" displays errors/warnings/success in\n'+"          red/orange/green.\n"+'  "text"  avoids color (e.g., useful in terminals not\n'+"          supporting color).\n"+'  "json"  emits json corresponding to the ValidationResult\n'+"          message in validator.proto.","color").parse(process.argv);const e=a.opts();if(e.length===0){a.outputHelp();process.exit(1)}if(e.html_format!=="AMP"&&e.html_format!=="AMP4ADS"&&e.html_format!=="AMP4EMAIL"){process.stderr.write('--html_format must be set to "AMP", "AMP4ADS", or "AMP4EMAIL".\n',(function(){process.exit(1)}))}if(e.format!=="color"&&e.format!=="text"&&e.format!=="json"){process.stderr.write('--format must be set to "color", "text", or "json".\n',(function(){process.exit(1)}))}const n=[];for(let t=0;t<a.args.length;t++){const r=a.args[t];if(r==="-"){n.push(readFromStdin())}else if(isHttpOrHttpsUrl(r)){n.push(readFromUrl(r,e.userAgent))}else{n.push(readFromFile(r))}}getInstance(e.validator_js,e.userAgent).then((function(r){l.all(n).then((function(t){const n={};let i=false;for(let s=0;s<t.length;s++){const o=r.validateString(t[s],e.html_format);if(e.format==="json"){n[a.args[s]]=o}else{logValidationResult(a.args[s],o,e.format==="color"?true:false)}if(o.status!=="PASS"){i=true}}if(e.format==="json"){process.stdout.write(JSON.stringify(n)+"\n",(function(){process.exit(i?1:0)}))}else if(i){process.stderr.write("",(function(){process.exit(1)}))}else{process.stdout.write("",(function(){process.exit(0)}))}})).catch((function(n){process.stderr.write((e.format=="color"?t.red(n.message):n.message)+"\n",(function(){process.exit(1)}))}))})).catch((function(n){process.stderr.write((e.format=="color"?t.red(n.message):n.message)+"\n",(function(){process.exit(1)}))}))}e.main=main})();module.exports=n})();
     |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              ^

  at IncomingMessage.<anonymous> (../packages/next/dist/compiled/amphtml-validator/index.js:17:846)
     Generating static pages (6/9) 
   ✓ Generating static pages (9/9)
  > Export encountered errors on following paths:
  	/info
  at ChildProcess.<anonymous> (lib/next-test-utils.ts:284:11)

● Export with default map › production mode › should export hybrid index amp page correctly

command failed with code 1 signal null
  ▲ Next.js 14.2.0-canary.43

   Linting and checking validity of types ...

 ⚠ The Next.js plugin was not detected in your ESLint configuration. See https://nextjs.org/docs/basic-features/eslint#migrating-existing-config
   Creating an optimized production build ...
 ✓ Compiled successfully
   Collecting page data ...
   Generating static pages (0/9) ...
   Generating static pages (2/9) 
   Generating static pages (4/9) 

Error occurred prerendering page "/info". Read more: https://nextjs.org/docs/messages/prerender-error

Error: Could not read from https://cdn.ampproject.org/v0/validator_wasm.js - aborted

  15 |  * See the License for the specific language governing permissions and
  16 |  * limitations under the license.
> 17 |  */const t=__nccwpck_require__(915);const r=__nccwpck_require__(147);const i=__nccwpck_require__(685);const s=__nccwpck_require__(687);const o=__nccwpck_require__(17);const a=__nccwpck_require__(642);const l=__nccwpck_require__(263);const u=__nccwpck_require__(477);const c=__nccwpck_require__(310);const h=__nccwpck_require__(837);const p=__nccwpck_require__(144);const f="amphtml-validator";function hasPrefix(e,t){return e.indexOf(t)==0}function isHttpOrHttpsUrl(e){return hasPrefix(e,"http://")||hasPrefix(e,"https://")}function readFromFile(e){return new l((function(t,n){r.readFile(e,"utf8",(function(e,r){if(e){n(e)}else{t(r.trim())}}))}))}function readFromReadable(e,t){return new l((function(n,r){const i=[];t.setEncoding("utf8");t.on("data",(function(e){i.push(e)}));t.on("end",(function(){n(i.join(""))}));t.on("error",(function(t){r(new Error("Could not read from "+e+" - "+t.message))}))}))}function readFromStdin(){return readFromReadable("stdin",process.stdin).then((function(e){process.stdin.resume();return e}))}function readFromUrl(e,t){return new l((function(n,r){const o=hasPrefix(e,"http://")?i:s;const a=o.request(e,(function(t){if(t.statusCode!==200){t.resume();r(new Error("Unable to fetch "+e+" - HTTP Status "+t.statusCode))}else{n(t)}}));a.setHeader("User-Agent",t);a.on("error",(function(t){r(new Error("Unable to fetch "+e+" - "+t.message))}));a.end()})).then(readFromReadable.bind(null,e))}function ValidationResult(){this.status="UNKNOWN";this.errors=[]}function ValidationError(){this.severity="UNKNOWN_SEVERITY";this.line=1;this.col=0;this.message="";this.specUrl=null;this.code="UNKNOWN_CODE";this.params=[]}function Validator(e){this.sandbox=p.createContext();try{new p.Script(e).runInContext(this.sandbox)}catch(e){throw new Error("Could not instantiate validator_wasm.js - "+e.message)}}Validator.prototype.init=function(){if(this.sandbox.amp.validator.init){return this.sandbox.amp.validator.init()}else{return l.resolve(undefined)}};Validator.prototype.validateString=function(e,t){const n=this.sandbox.amp.validator.validateString(e,t);const r=new ValidationResult;r.status=n.status;for(let e=0;e<n.errors.length;e++){const t=n.errors[e];const i=new ValidationError;i.severity=t.severity;i.line=t.line;i.col=t.col;i.message=this.sandbox.amp.validator.renderErrorMessage(t);i.specUrl=t.specUrl;i.code=t.code;i.params=t.params;r.errors.push(i)}return r};const m={};function getInstance(e,t){const n=e||"https://cdn.ampproject.org/v0/validator_wasm.js";const r=t||f;if(m.hasOwnProperty(n)){return l.resolve(m[n])}const i=isHttpOrHttpsUrl(n)?readFromUrl(n,r):readFromFile(n);return i.then((function(e){let t;try{t=new Validator(e)}catch(e){throw e}m[n]=t;return t})).then((function(e){return e.init().then((()=>e))}))}e.getInstance=getInstance;function newInstance(e){return new Validator(e)}e.newInstance=newInstance;function logValidationResult(e,n,r){if(n.status==="PASS"){process.stdout.write(e+": "+(r?t.green("PASS"):"PASS")+"\n")}for(let i=0;i<n.errors.length;i++){const s=n.errors[i];let o=e+":"+s.line+":"+s.col+" ";if(r){o+=(s.severity==="ERROR"?t.red:t.magenta)(s.message)}else{o+=s.message}if(s.specUrl){o+=" (see "+s.specUrl+")"}process.stderr.write(o+"\n")}}function main(){a.usage("[options] <fileOrUrlOrMinus...>\n\n"+'  Validates the files or urls provided as arguments. If "-" is\n'+"  specified, reads from stdin instead.").option("--validator_js <fileOrUrl>","The Validator Javascript.\n"+"  Latest published version by default, or\n"+"  dist/validator_minified.js (built with build.py)\n"+"  for development.","https://cdn.ampproject.org/v0/validator_wasm.js").option("--user-agent <userAgent>","User agent string to use in requests.",f).option("--html_format <AMP|AMP4ADS|AMP4EMAIL>","The input format to be validated.\n"+"  AMP by default.","AMP").option("--format <color|text|json>","How to format the output.\n"+'  "color" displays errors/warnings/success in\n'+"          red/orange/green.\n"+'  "text"  avoids color (e.g., useful in terminals not\n'+"          supporting color).\n"+'  "json"  emits json corresponding to the ValidationResult\n'+"          message in validator.proto.","color").parse(process.argv);const e=a.opts();if(e.length===0){a.outputHelp();process.exit(1)}if(e.html_format!=="AMP"&&e.html_format!=="AMP4ADS"&&e.html_format!=="AMP4EMAIL"){process.stderr.write('--html_format must be set to "AMP", "AMP4ADS", or "AMP4EMAIL".\n',(function(){process.exit(1)}))}if(e.format!=="color"&&e.format!=="text"&&e.format!=="json"){process.stderr.write('--format must be set to "color", "text", or "json".\n',(function(){process.exit(1)}))}const n=[];for(let t=0;t<a.args.length;t++){const r=a.args[t];if(r==="-"){n.push(readFromStdin())}else if(isHttpOrHttpsUrl(r)){n.push(readFromUrl(r,e.userAgent))}else{n.push(readFromFile(r))}}getInstance(e.validator_js,e.userAgent).then((function(r){l.all(n).then((function(t){const n={};let i=false;for(let s=0;s<t.length;s++){const o=r.validateString(t[s],e.html_format);if(e.format==="json"){n[a.args[s]]=o}else{logValidationResult(a.args[s],o,e.format==="color"?true:false)}if(o.status!=="PASS"){i=true}}if(e.format==="json"){process.stdout.write(JSON.stringify(n)+"\n",(function(){process.exit(i?1:0)}))}else if(i){process.stderr.write("",(function(){process.exit(1)}))}else{process.stdout.write("",(function(){process.exit(0)}))}})).catch((function(n){process.stderr.write((e.format=="color"?t.red(n.message):n.message)+"\n",(function(){process.exit(1)}))}))})).catch((function(n){process.stderr.write((e.format=="color"?t.red(n.message):n.message)+"\n",(function(){process.exit(1)}))}))}e.main=main})();module.exports=n})();
     |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              ^

  at IncomingMessage.<anonymous> (../packages/next/dist/compiled/amphtml-validator/index.js:17:846)
     Generating static pages (6/9) 
   ✓ Generating static pages (9/9)
  > Export encountered errors on following paths:
  	/info
  at ChildProcess.<anonymous> (lib/next-test-utils.ts:284:11)

Read more about building and testing Next.js in contributing.md.

@wyattjoh wyattjoh merged commit 4ee00d4 into canary Mar 26, 2024
77 checks passed
@wyattjoh wyattjoh deleted the feat/ppr-error-stacks branch March 26, 2024 18:19
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 10, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants