@angular-schule/prerender-format 0.2.0 → 0.2.1
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.
|
@@ -51,7 +51,7 @@ function getPrerenderCalls() {
|
|
|
51
51
|
return prerenderCalls;
|
|
52
52
|
}
|
|
53
53
|
function setReservedFiles(files) {
|
|
54
|
-
reservedFiles = files
|
|
54
|
+
reservedFiles = files;
|
|
55
55
|
}
|
|
56
56
|
function toFilePath(outPath) {
|
|
57
57
|
if (!outPath.endsWith(INDEX_SUFFIX)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-format.js","sourceRoot":"","sources":["../../application/file-format.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\n/** A prerendered page as returned by `prerenderPages()` of `@angular/build`. */\nexport interface PrerenderedFile {\n content: string;\n appShellRoute: boolean;\n}\n\nexport type PrerenderOutput = Record<string, PrerenderedFile>;\n\nexport interface PrerenderResult {\n output: PrerenderOutput;\n warnings?: string[];\n [key: string]: unknown;\n}\n\nexport type PrerenderPages = (...args: unknown[]) => Promise<PrerenderResult>;\n\nexport interface FileOutputResult {\n output: PrerenderOutput;\n warnings: string[];\n}\n\nconst INDEX_FILE = 'index.html';\nconst INDEX_SUFFIX = '/' + INDEX_FILE;\nconst PATCHED = Symbol.for('@angular-schule/prerender-format:patched');\nconst PRERENDER_MODULE = 'src/utils/server-rendering/prerender.js';\n\nlet prerenderCalls = 0;\nlet reservedFiles: string[] = [];\n\n/** Number of `prerenderPages()` calls that went through the wrapper in this process. */\nexport function getPrerenderCalls(): number {\n return prerenderCalls;\n}\n\n/** Top-level file names that a route must not take over, such as the CSR index `index.csr.html`. */\nexport function setReservedFiles(files: string[]): void {\n reservedFiles = files.map(file => file.toLowerCase());\n}\n\n/**\n * Maps a prerender output path from `foo/index.html` to `foo.html`.\n * The root `index.html` of a build (start page, or locale start page with its base href) stays as is.\n */\nexport function toFilePath(outPath: string): string {\n if (!outPath.endsWith(INDEX_SUFFIX)) {\n return outPath;\n }\n\n return outPath.slice(0, -INDEX_SUFFIX.length) + '.html';\n}\n\n/** Route of an output path, for messages: `blog/index/index.html` → `/blog/index`. */\nfunction routeOf(outPath: string): string {\n if (outPath === INDEX_FILE) {\n return '/';\n }\n\n return '/' + (outPath.endsWith(INDEX_SUFFIX) ? outPath.slice(0, -INDEX_SUFFIX.length) : outPath);\n}\n\n/**\n * Renames all keys of a prerender `output` record from `foo/index.html` to `foo.html`.\n * A route keeps `foo/index.html` (with a warning) when `foo.html` is not safe:\n * - its last segment is `index` in any letter case: `index.html` is served for the parent path,\n * and on macOS and Windows `Index.html` is the same file as `index.html`,\n * - the file name is reserved, such as the CSR index `index.csr.html`,\n * - another route already uses the file name (compared case-insensitively).\n */\nexport function toFileOutput(output: PrerenderOutput, reserved: string[] = reservedFiles): FileOutputResult {\n const renamed: PrerenderOutput = {};\n const warnings: string[] = [];\n const usedFiles = new Map<string, string>();\n const reservedLower = reserved.map(file => file.toLowerCase());\n\n for (const [outPath, file] of Object.entries(output)) {\n const route = routeOf(outPath);\n const filePath = toFilePath(outPath);\n const lowerFilePath = filePath.toLowerCase();\n const lowerOutPath = outPath.toLowerCase();\n\n let reason: string | undefined;\n if (filePath !== outPath) {\n if (lowerOutPath.endsWith('/index' + INDEX_SUFFIX) || lowerOutPath === 'index' + INDEX_SUFFIX) {\n reason = `'${filePath}' would be served for '${route.slice(0, -'index'.length)}'`;\n } else if (reservedLower.includes(lowerFilePath)) {\n reason = `'${filePath}' is used by the build itself`;\n } else if (usedFiles.has(lowerFilePath)) {\n reason = `'${filePath}' is already used by route '${usedFiles.get(lowerFilePath)}'`;\n }\n }\n\n if (reason) {\n warnings.push(`Route '${route}' is written to '${outPath}' instead, because ${reason}.`);\n renamed[outPath] = file;\n continue;\n }\n\n usedFiles.set(lowerFilePath, route);\n renamed[filePath] = file;\n }\n\n return { output: renamed, warnings };\n}\n\n/**\n * Wraps `prerenderPages` so that its `output` record uses the \"file\" format.\n * Routes that keep the directory format are reported as build warnings of `prerenderPages`.\n * An unknown result is passed through unchanged.\n */\nexport function wrapPrerenderPages(prerenderPages: PrerenderPages): PrerenderPages {\n const wrapped = async function (this: unknown, ...args: unknown[]) {\n const result = await prerenderPages.apply(this, args);\n if (\n !result ||\n typeof result.output !== 'object' ||\n result.output === null ||\n !Array.isArray(result.warnings)\n ) {\n return result;\n }\n prerenderCalls++;\n\n const { output, warnings } = toFileOutput(result.output);\n\n return { ...result, output, warnings: [...(result.warnings as string[]), ...warnings] };\n };\n Object.defineProperty(wrapped, PATCHED, { value: true });\n\n return wrapped;\n}\n\nfunction isPatched(fn: PrerenderPages): boolean {\n return (fn as unknown as Record<symbol, unknown>)[PATCHED] === true;\n}\n\nexport type InstallResult = { installed: true } | { installed: false; reason: string };\n\n/**\n * Replaces `prerenderPages` of the given `@angular/build` installation.\n * `execute-post-bundle.js` reads the function from the module's exports object on every call,\n * so the replacement takes effect for regular and localized builds alike.\n * Returns the reason instead of replacing anything when the internals are not as expected.\n */\nexport function installFileFormat(angularBuildRoot: string): InstallResult {\n const modulePath = path.join(angularBuildRoot, PRERENDER_MODULE);\n if (!fs.existsSync(modulePath)) {\n return { installed: false, reason: `'${modulePath}' does not exist. This version of @angular/build is not supported.` };\n }\n\n const prerenderModule: { prerenderPages?: PrerenderPages } = require(modulePath);\n const descriptor = Object.getOwnPropertyDescriptor(prerenderModule, 'prerenderPages');\n const prerenderPages = prerenderModule.prerenderPages;\n if (typeof prerenderPages !== 'function' || !descriptor?.writable) {\n return {\n installed: false,\n reason: `'${modulePath}' exports no replaceable prerenderPages(). This version of @angular/build is not supported.`\n };\n }\n\n if (!isPatched(prerenderPages)) {\n prerenderModule.prerenderPages = wrapPrerenderPages(prerenderPages);\n }\n\n return { installed: true };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"file-format.js","sourceRoot":"","sources":["../../application/file-format.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,8CAEC;AAGD,4CAEC;AAMD,gCAMC;AAmBD,oCAkCC;AAOD,gDAoBC;AAcD,8CAqBC;AAvKD,uCAAyB;AACzB,2CAA6B;AAuB7B,MAAM,UAAU,GAAG,YAAY,CAAC;AAChC,MAAM,YAAY,GAAG,GAAG,GAAG,UAAU,CAAC;AACtC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;AACvE,MAAM,gBAAgB,GAAG,yCAAyC,CAAC;AAEnE,IAAI,cAAc,GAAG,CAAC,CAAC;AACvB,IAAI,aAAa,GAAa,EAAE,CAAC;AAGjC,SAAgB,iBAAiB;IAC/B,OAAO,cAAc,CAAC;AACxB,CAAC;AAGD,SAAgB,gBAAgB,CAAC,KAAe;IAC9C,aAAa,GAAG,KAAK,CAAC;AACxB,CAAC;AAMD,SAAgB,UAAU,CAAC,OAAe;IACxC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QACpC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC;AAC1D,CAAC;AAGD,SAAS,OAAO,CAAC,OAAe;IAC9B,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;QAC3B,OAAO,GAAG,CAAC;IACb,CAAC;IAED,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;AACnG,CAAC;AAUD,SAAgB,YAAY,CAAC,MAAuB,EAAE,WAAqB,aAAa;IACtF,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC5C,MAAM,aAAa,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAE/D,KAAK,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACrD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAC/B,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,aAAa,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;QAC7C,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;QAE3C,IAAI,MAA0B,CAAC;QAC/B,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YACzB,IAAI,YAAY,CAAC,QAAQ,CAAC,QAAQ,GAAG,YAAY,CAAC,IAAI,YAAY,KAAK,OAAO,GAAG,YAAY,EAAE,CAAC;gBAC9F,MAAM,GAAG,IAAI,QAAQ,0BAA0B,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC;YACpF,CAAC;iBAAM,IAAI,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;gBACjD,MAAM,GAAG,IAAI,QAAQ,+BAA+B,CAAC;YACvD,CAAC;iBAAM,IAAI,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;gBACxC,MAAM,GAAG,IAAI,QAAQ,+BAA+B,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC;YACtF,CAAC;QACH,CAAC;QAED,IAAI,MAAM,EAAE,CAAC;YACX,QAAQ,CAAC,IAAI,CAAC,UAAU,KAAK,oBAAoB,OAAO,sBAAsB,MAAM,GAAG,CAAC,CAAC;YACzF,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;YACxB,SAAS;QACX,CAAC;QAED,SAAS,CAAC,GAAG,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;QACpC,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IAC3B,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AACvC,CAAC;AAOD,SAAgB,kBAAkB,CAAC,cAA8B;IAC/D,MAAM,OAAO,GAAG,KAAK,WAA0B,GAAG,IAAe;QAC/D,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACtD,IACE,CAAC,MAAM;YACP,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ;YACjC,MAAM,CAAC,MAAM,KAAK,IAAI;YACtB,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAC/B,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,cAAc,EAAE,CAAC;QAEjB,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAEzD,OAAO,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAI,MAAM,CAAC,QAAqB,EAAE,GAAG,QAAQ,CAAC,EAAE,CAAC;IAC1F,CAAC,CAAC;IACF,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAEzD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,SAAS,CAAC,EAAkB;IACnC,OAAQ,EAAyC,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;AACtE,CAAC;AAUD,SAAgB,iBAAiB,CAAC,gBAAwB;IACxD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;IACjE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,UAAU,oEAAoE,EAAE,CAAC;IAC1H,CAAC;IAED,MAAM,eAAe,GAAwC,OAAO,CAAC,UAAU,CAAC,CAAC;IACjF,MAAM,UAAU,GAAG,MAAM,CAAC,wBAAwB,CAAC,eAAe,EAAE,gBAAgB,CAAC,CAAC;IACtF,MAAM,cAAc,GAAG,eAAe,CAAC,cAAc,CAAC;IACtD,IAAI,OAAO,cAAc,KAAK,UAAU,IAAI,CAAC,UAAU,EAAE,QAAQ,EAAE,CAAC;QAClE,OAAO;YACL,SAAS,EAAE,KAAK;YAChB,MAAM,EAAE,IAAI,UAAU,6FAA6F;SACpH,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE,CAAC;QAC/B,eAAe,CAAC,cAAc,GAAG,kBAAkB,CAAC,cAAc,CAAC,CAAC;IACtE,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AAC7B,CAAC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\n/** A prerendered page as returned by `prerenderPages()` of `@angular/build`. */\nexport interface PrerenderedFile {\n content: string;\n appShellRoute: boolean;\n}\n\nexport type PrerenderOutput = Record<string, PrerenderedFile>;\n\nexport interface PrerenderResult {\n output: PrerenderOutput;\n warnings?: string[];\n [key: string]: unknown;\n}\n\nexport type PrerenderPages = (...args: unknown[]) => Promise<PrerenderResult>;\n\nexport interface FileOutputResult {\n output: PrerenderOutput;\n warnings: string[];\n}\n\nconst INDEX_FILE = 'index.html';\nconst INDEX_SUFFIX = '/' + INDEX_FILE;\nconst PATCHED = Symbol.for('@angular-schule/prerender-format:patched');\nconst PRERENDER_MODULE = 'src/utils/server-rendering/prerender.js';\n\nlet prerenderCalls = 0;\nlet reservedFiles: string[] = [];\n\n/** Number of `prerenderPages()` calls that went through the wrapper in this process. */\nexport function getPrerenderCalls(): number {\n return prerenderCalls;\n}\n\n/** Top-level file names that a route must not take over, such as the CSR index `index.csr.html`. */\nexport function setReservedFiles(files: string[]): void {\n reservedFiles = files;\n}\n\n/**\n * Maps a prerender output path from `foo/index.html` to `foo.html`.\n * The root `index.html` of a build (start page, or locale start page with its base href) stays as is.\n */\nexport function toFilePath(outPath: string): string {\n if (!outPath.endsWith(INDEX_SUFFIX)) {\n return outPath;\n }\n\n return outPath.slice(0, -INDEX_SUFFIX.length) + '.html';\n}\n\n/** Route of an output path, for messages: `blog/index/index.html` → `/blog/index`. */\nfunction routeOf(outPath: string): string {\n if (outPath === INDEX_FILE) {\n return '/';\n }\n\n return '/' + (outPath.endsWith(INDEX_SUFFIX) ? outPath.slice(0, -INDEX_SUFFIX.length) : outPath);\n}\n\n/**\n * Renames all keys of a prerender `output` record from `foo/index.html` to `foo.html`.\n * A route keeps `foo/index.html` (with a warning) when `foo.html` is not safe:\n * - its last segment is `index` in any letter case: `index.html` is served for the parent path,\n * and on macOS and Windows `Index.html` is the same file as `index.html`,\n * - the file name is reserved, such as the CSR index `index.csr.html`,\n * - another route already uses the file name (compared case-insensitively).\n */\nexport function toFileOutput(output: PrerenderOutput, reserved: string[] = reservedFiles): FileOutputResult {\n const renamed: PrerenderOutput = {};\n const warnings: string[] = [];\n const usedFiles = new Map<string, string>();\n const reservedLower = reserved.map(file => file.toLowerCase());\n\n for (const [outPath, file] of Object.entries(output)) {\n const route = routeOf(outPath);\n const filePath = toFilePath(outPath);\n const lowerFilePath = filePath.toLowerCase();\n const lowerOutPath = outPath.toLowerCase();\n\n let reason: string | undefined;\n if (filePath !== outPath) {\n if (lowerOutPath.endsWith('/index' + INDEX_SUFFIX) || lowerOutPath === 'index' + INDEX_SUFFIX) {\n reason = `'${filePath}' would be served for '${route.slice(0, -'index'.length)}'`;\n } else if (reservedLower.includes(lowerFilePath)) {\n reason = `'${filePath}' is used by the build itself`;\n } else if (usedFiles.has(lowerFilePath)) {\n reason = `'${filePath}' is already used by route '${usedFiles.get(lowerFilePath)}'`;\n }\n }\n\n if (reason) {\n warnings.push(`Route '${route}' is written to '${outPath}' instead, because ${reason}.`);\n renamed[outPath] = file;\n continue;\n }\n\n usedFiles.set(lowerFilePath, route);\n renamed[filePath] = file;\n }\n\n return { output: renamed, warnings };\n}\n\n/**\n * Wraps `prerenderPages` so that its `output` record uses the \"file\" format.\n * Routes that keep the directory format are reported as build warnings of `prerenderPages`.\n * An unknown result is passed through unchanged.\n */\nexport function wrapPrerenderPages(prerenderPages: PrerenderPages): PrerenderPages {\n const wrapped = async function (this: unknown, ...args: unknown[]) {\n const result = await prerenderPages.apply(this, args);\n if (\n !result ||\n typeof result.output !== 'object' ||\n result.output === null ||\n !Array.isArray(result.warnings)\n ) {\n return result;\n }\n prerenderCalls++;\n\n const { output, warnings } = toFileOutput(result.output);\n\n return { ...result, output, warnings: [...(result.warnings as string[]), ...warnings] };\n };\n Object.defineProperty(wrapped, PATCHED, { value: true });\n\n return wrapped;\n}\n\nfunction isPatched(fn: PrerenderPages): boolean {\n return (fn as unknown as Record<symbol, unknown>)[PATCHED] === true;\n}\n\nexport type InstallResult = { installed: true } | { installed: false; reason: string };\n\n/**\n * Replaces `prerenderPages` of the given `@angular/build` installation.\n * `execute-post-bundle.js` reads the function from the module's exports object on every call,\n * so the replacement takes effect for regular and localized builds alike.\n * Returns the reason instead of replacing anything when the internals are not as expected.\n */\nexport function installFileFormat(angularBuildRoot: string): InstallResult {\n const modulePath = path.join(angularBuildRoot, PRERENDER_MODULE);\n if (!fs.existsSync(modulePath)) {\n return { installed: false, reason: `'${modulePath}' does not exist. This version of @angular/build is not supported.` };\n }\n\n const prerenderModule: { prerenderPages?: PrerenderPages } = require(modulePath);\n const descriptor = Object.getOwnPropertyDescriptor(prerenderModule, 'prerenderPages');\n const prerenderPages = prerenderModule.prerenderPages;\n if (typeof prerenderPages !== 'function' || !descriptor?.writable) {\n return {\n installed: false,\n reason: `'${modulePath}' exports no replaceable prerenderPages(). This version of @angular/build is not supported.`\n };\n }\n\n if (!isPatched(prerenderPages)) {\n prerenderModule.prerenderPages = wrapPrerenderPages(prerenderPages);\n }\n\n return { installed: true };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular-schule/prerender-format",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Angular application builder that writes prerendered routes as foo.html instead of foo/index.html (no trailing slash redirects)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|