@analogjs/platform 3.0.0-alpha.64 → 3.0.0-alpha.66
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.
- package/package.json +4 -3
- package/src/lib/clear-client-page-endpoint.d.ts +1 -1
- package/src/lib/clear-client-page-endpoint.js +26 -5
- package/src/lib/clear-client-page-endpoint.js.map +1 -1
- package/src/lib/deps-plugin.js +17 -10
- package/src/lib/deps-plugin.js.map +1 -1
- package/src/lib/nitro/analog-nitro-plugin.d.ts +10 -0
- package/src/lib/nitro/analog-nitro-plugin.js +54 -16
- package/src/lib/nitro/analog-nitro-plugin.js.map +1 -1
- package/src/lib/nitro/get-content-files.js +1 -1
- package/src/lib/nitro/get-page-handlers.d.ts +6 -7
- package/src/lib/nitro/get-page-handlers.js +7 -8
- package/src/lib/nitro/get-page-handlers.js.map +1 -1
- package/src/lib/nitro/renderers.d.ts +0 -20
- package/src/lib/nitro/renderers.js.map +1 -1
- package/src/lib/nx-plugin/src/generators/app/files/agents/AGENTS.md +11 -0
- package/src/lib/nx-plugin/src/generators/app/files/agents/CLAUDE.md +1 -0
- package/src/lib/nx-plugin/src/generators/app/generator.js +2 -0
- package/src/lib/nx-plugin/src/generators/app/generator.js.map +1 -1
- package/src/lib/nx-plugin/src/generators/app/lib/add-agent-context.d.ts +2 -0
- package/src/lib/nx-plugin/src/generators/app/lib/add-agent-context.js +16 -0
- package/src/lib/nx-plugin/src/generators/app/lib/add-agent-context.js.map +1 -0
- package/src/lib/nx-plugin/src/generators/app/schema.d.ts +2 -0
- package/src/lib/nx-plugin/src/generators/app/versions/nx_18_X/versions.d.ts +5 -5
- package/src/lib/nx-plugin/src/generators/app/versions/nx_18_X/versions.js.map +1 -1
- package/src/lib/nx-plugin/src/generators/preset/generator.js +6 -1
- package/src/lib/nx-plugin/src/generators/preset/generator.js.map +1 -1
- package/src/lib/nx-plugin/src/utils/versions/ng_19_X/versions.d.ts +5 -5
- package/src/lib/nx-plugin/src/utils/versions/ng_19_X/versions.js +5 -5
- package/src/lib/nx-plugin/src/utils/versions/ng_19_X/versions.js.map +1 -1
- package/src/lib/options.d.ts +6 -0
- package/src/lib/platform-plugin.js +3 -1
- package/src/lib/platform-plugin.js.map +1 -1
- package/src/lib/server-fn-client-transform.d.ts +26 -0
- package/src/lib/server-fn-client-transform.js +97 -0
- package/src/lib/server-fn-client-transform.js.map +1 -0
- package/src/lib/ssr/defer-streaming-plugin.d.ts +65 -0
- package/src/lib/ssr/inject-html-plugin.js +1 -1
- package/src/lib/utils/debug-harness.js +1 -1
|
@@ -7,23 +7,22 @@ import { globSync } from "tinyglobby";
|
|
|
7
7
|
*
|
|
8
8
|
* Discovers all `.server.ts` files under `app/pages/**` and any additional
|
|
9
9
|
* pages directories, then maps each file to a Nitro route pattern under
|
|
10
|
-
* `/_analog/pages
|
|
10
|
+
* `/api/_analog/pages/...`.
|
|
11
11
|
*
|
|
12
12
|
* Route transformation examples:
|
|
13
|
-
* - index.server.ts → /_analog/pages/index
|
|
14
|
-
* - users/[id].server.ts → /_analog/pages/users/:id
|
|
15
|
-
* - products/[...slug].server.ts → /_analog/pages/products/**:slug
|
|
16
|
-
* - (auth)/login.server.ts → /_analog/pages/-auth-/login
|
|
13
|
+
* - index.server.ts → /api/_analog/pages/index
|
|
14
|
+
* - users/[id].server.ts → /api/_analog/pages/users/:id
|
|
15
|
+
* - products/[...slug].server.ts → /api/_analog/pages/products/**:slug
|
|
16
|
+
* - (auth)/login.server.ts → /api/_analog/pages/-auth-/login
|
|
17
17
|
*/
|
|
18
|
-
function getPageHandlers({ workspaceRoot, sourceRoot, rootDir, additionalPagesDirs
|
|
18
|
+
function getPageHandlers({ workspaceRoot, sourceRoot, rootDir, additionalPagesDirs }) {
|
|
19
19
|
return globSync([`${normalizePath(resolve(workspaceRoot, rootDir))}/${sourceRoot}/app/pages/**/*.server.ts`, ...(additionalPagesDirs || []).map((dir) => `${workspaceRoot}${dir}/**/*.server.ts`)], {
|
|
20
20
|
dot: true,
|
|
21
21
|
absolute: true
|
|
22
22
|
}).map((endpointFile) => {
|
|
23
|
-
const route = normalizePath(endpointFile).replace(/^(.*?)\/pages/, "/pages").replace(/\.server\.ts$/, "").replace(/\[\.{3}(.+)\]/g, "**:$1").replace(/\[\.{3}(\w+)\]/g, "**:$1").replace(/\/\(([^/]+)\)/g, "/-$1-").replace(/\[(\w+)\]/g, ":$1").replace(/\./g, "/");
|
|
24
23
|
return {
|
|
25
24
|
handler: endpointFile,
|
|
26
|
-
route:
|
|
25
|
+
route: `/api/_analog${normalizePath(endpointFile).replace(/^(.*?)\/pages/, "/pages").replace(/\.server\.ts$/, "").replace(/\[\.{3}(.+)\]/g, "**:$1").replace(/\[\.{3}(\w+)\]/g, "**:$1").replace(/\/\(([^/]+)\)/g, "/-$1-").replace(/\[(\w+)\]/g, ":$1").replace(/\./g, "/")}`,
|
|
27
26
|
lazy: true
|
|
28
27
|
};
|
|
29
28
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-page-handlers.js","names":[],"sources":["../../../../src/lib/nitro/get-page-handlers.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport { globSync } from 'tinyglobby';\n\nimport type { NitroEventHandler } from 'nitro/types';\nimport { normalizePath } from 'vite';\n\ntype GetHandlersArgs = {\n workspaceRoot: string;\n sourceRoot: string;\n rootDir: string;\n additionalPagesDirs?: string[];\n
|
|
1
|
+
{"version":3,"file":"get-page-handlers.js","names":[],"sources":["../../../../src/lib/nitro/get-page-handlers.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport { globSync } from 'tinyglobby';\n\nimport type { NitroEventHandler } from 'nitro/types';\nimport { normalizePath } from 'vite';\n\ntype GetHandlersArgs = {\n workspaceRoot: string;\n sourceRoot: string;\n rootDir: string;\n additionalPagesDirs?: string[];\n};\n\n/**\n * Discovers and generates Nitro event handlers for server-side page routes.\n *\n * Discovers all `.server.ts` files under `app/pages/**` and any additional\n * pages directories, then maps each file to a Nitro route pattern under\n * `/api/_analog/pages/...`.\n *\n * Route transformation examples:\n * - index.server.ts → /api/_analog/pages/index\n * - users/[id].server.ts → /api/_analog/pages/users/:id\n * - products/[...slug].server.ts → /api/_analog/pages/products/**:slug\n * - (auth)/login.server.ts → /api/_analog/pages/-auth-/login\n */\nexport function getPageHandlers({\n workspaceRoot,\n sourceRoot,\n rootDir,\n additionalPagesDirs,\n}: GetHandlersArgs): NitroEventHandler[] {\n const root = normalizePath(resolve(workspaceRoot, rootDir));\n\n const endpointFiles: string[] = globSync(\n [\n `${root}/${sourceRoot}/app/pages/**/*.server.ts`,\n ...(additionalPagesDirs || []).map(\n (dir) => `${workspaceRoot}${dir}/**/*.server.ts`,\n ),\n ],\n { dot: true, absolute: true },\n );\n\n const handlers: NitroEventHandler[] = endpointFiles.map((endpointFile) => {\n const normalized = normalizePath(endpointFile);\n const route = normalized\n .replace(/^(.*?)\\/pages/, '/pages')\n .replace(/\\.server\\.ts$/, '')\n .replace(/\\[\\.{3}(.+)\\]/g, '**:$1')\n .replace(/\\[\\.{3}(\\w+)\\]/g, '**:$1')\n // Strip Angular Router group syntax `(group)` from any segment, not\n // just trailing ones. Routes like `(auth)/login.server.ts` need to\n // become `/-auth-/login`, otherwise the literal parens leak through\n // and the handler is mounted under an invalid Nitro path.\n .replace(/\\/\\(([^/]+)\\)/g, '/-$1-')\n .replace(/\\[(\\w+)\\]/g, ':$1')\n .replace(/\\./g, '/');\n\n return {\n handler: endpointFile,\n route: `/api/_analog${route}`,\n lazy: true,\n };\n });\n\n return handlers;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA0BA,SAAgB,gBAAgB,EAC9B,eACA,YACA,SACA,uBACuC;AAmCvC,QAhCgC,SAC9B,CACE,GAJS,cAAc,QAAQ,eAAe,QAAQ,CAAC,CAI/C,GAAG,WAAW,4BACtB,IAAI,uBAAuB,EAAE,EAAE,KAC5B,QAAQ,GAAG,gBAAgB,IAAI,iBACjC,CACF,EACD;EAAE,KAAK;EAAM,UAAU;EAAM,CAC9B,CAEmD,KAAK,iBAAiB;AAexE,SAAO;GACL,SAAS;GACT,OAAO,eAhBU,cAAc,aAAa,CAE3C,QAAQ,iBAAiB,SAAS,CAClC,QAAQ,iBAAiB,GAAG,CAC5B,QAAQ,kBAAkB,QAAQ,CAClC,QAAQ,mBAAmB,QAAQ,CAKnC,QAAQ,kBAAkB,QAAQ,CAClC,QAAQ,cAAc,MAAM,CAC5B,QAAQ,OAAO,IAAI;GAKpB,MAAM;GACP;GACD"}
|
|
@@ -31,23 +31,3 @@ export declare function ssrRenderer(): string;
|
|
|
31
31
|
* for every route, letting the client-side Angular router handle navigation.
|
|
32
32
|
*/
|
|
33
33
|
export declare function clientRenderer(): string;
|
|
34
|
-
/**
|
|
35
|
-
* API middleware virtual module content.
|
|
36
|
-
*
|
|
37
|
-
* Intercepts requests matching the configured API prefix and either:
|
|
38
|
-
* - Uses event-bound internal forwarding for GET requests (except .xml routes)
|
|
39
|
-
* - Uses request proxying for all other methods to forward the full request
|
|
40
|
-
*
|
|
41
|
-
* h3 v2 idiomatic APIs used:
|
|
42
|
-
* - defineHandler (replaces defineEventHandler / eventHandler)
|
|
43
|
-
* - event.path (replaces event.node.req.url)
|
|
44
|
-
* - event.method (replaces event.node.req.method)
|
|
45
|
-
* - proxyRequest is retained internally because it preserves Nitro route
|
|
46
|
-
* matching for event-bound server requests during SSR/prerender
|
|
47
|
-
* - Object.fromEntries(event.req.headers.entries()) replaces direct event.node.req.headers access
|
|
48
|
-
*
|
|
49
|
-
* `fetchWithEvent` keeps the active event context while forwarding to a
|
|
50
|
-
* rewritten path, which avoids falling through to the HTML renderer when
|
|
51
|
-
* SSR code makes relative API requests.
|
|
52
|
-
*/
|
|
53
|
-
export declare const apiMiddleware = "\nimport { defineHandler, fetchWithEvent, proxyRequest } from 'nitro/h3';\nimport { useRuntimeConfig } from 'nitro/runtime-config';\n\nexport default defineHandler(async (event) => {\n const prefix = useRuntimeConfig().prefix;\n const apiPrefix = `${prefix}/${useRuntimeConfig().apiPrefix}`;\n\n // Match the configured prefix as a full path segment. A bare\n // startsWith would false-match /apiary against an apiPrefix of /api.\n if (\n event.path === apiPrefix ||\n event.path?.startsWith(apiPrefix + '/')\n ) {\n const reqUrl = event.path?.replace(apiPrefix, '');\n\n if (\n event.method === 'GET' &&\n // in the case of XML routes, we want to proxy the request so that nitro gets the correct headers\n // and can render the XML correctly as a static asset\n !event.path?.endsWith('.xml')\n ) {\n return fetchWithEvent(event, reqUrl, {\n headers: Object.fromEntries(event.req.headers.entries()),\n });\n }\n\n return proxyRequest(event, reqUrl);\n }\n});";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderers.js","names":[],"sources":["../../../../src/lib/nitro/renderers.ts"],"sourcesContent":["/**\n * Code snippet emitted into virtual modules to create a request-scoped\n * fetch using ofetch's `createFetch` + h3's `fetchWithEvent`.\n *\n * Shared between the SSR renderer and page-endpoint virtual modules so\n * the fetch-wiring logic stays in sync.\n *\n * The emitted variable is named `serverFetch` — callers should reference it\n * by that name.\n */\nexport const SERVER_FETCH_FACTORY_SNIPPET = `\n const serverFetch = createFetch({\n fetch: (resource, init) => {\n const url = resource instanceof Request ? resource.url : resource.toString();\n return fetchWithEvent(event, url, init);\n }\n });`;\n\n/**\n * SSR renderer virtual module content.\n *\n * This code runs inside Nitro's server runtime (Node.js context) where\n * event.node is always populated. In h3 v2, event.node is typed as optional,\n * so we use h3's first-class event properties (event.path, event.method) where\n * possible and apply optional chaining when accessing the Node.js context for\n * the Angular renderer which requires raw req/res objects.\n *\n * h3 v2 idiomatic APIs used:\n * - defineHandler (replaces defineEventHandler / eventHandler)\n * - event.path (replaces event.node.req.url)\n * - getResponseHeader compat shim (still available in h3 v2)\n */\nexport function ssrRenderer() {\n return `\nimport { createFetch } from 'ofetch';\nimport { defineHandler, fetchWithEvent } from 'nitro/h3';\n// @ts-ignore\nimport renderer from '#analog/ssr';\nimport template from '#analog/index';\n\nconst normalizeHtmlRequestUrl = (url) =>\n url.replace(/\\\\/index\\\\.html(?=$|[?#])/, '/');\n\nexport default defineHandler(async (event) => {\n event.res.headers.set('content-type', 'text/html; charset=utf-8');\n const noSSR = event.res.headers.get('x-analog-no-ssr');\n const requestPath = normalizeHtmlRequestUrl(event.path);\n\n if (noSSR === 'true') {\n return template;\n }\n\n // event.path is the canonical h3 v2 way to access the request URL.\n // event.node?.req and event.node?.res are needed by the Angular SSR renderer\n // which operates on raw Node.js request/response objects.\n // During prerendering (Nitro v3 fetch-based pipeline), event.node is undefined.\n // The Angular renderer requires a req object with at least { headers, url },\n // so we provide a minimal stub to avoid runtime errors in prerender context.\n const req = event.node?.req\n ? {\n ...event.node.req,\n url: requestPath,\n originalUrl: requestPath,\n }\n : {\n headers: { host: 'localhost' },\n url: requestPath,\n originalUrl: requestPath,\n connection: {},\n };\n const res = event.node?.res;\n${SERVER_FETCH_FACTORY_SNIPPET}\n\n const html = await renderer(requestPath, template, { req, res, fetch: serverFetch });\n\n return html;\n});`;\n}\n\n/**\n * Client-only renderer virtual module content.\n *\n * Used when SSR is disabled — simply serves the static index.html template\n * for every route, letting the client-side Angular router handle navigation.\n */\nexport function clientRenderer() {\n return `\nimport { defineHandler } from 'nitro/h3';\nimport template from '#analog/index';\n\nexport default defineHandler(async (event) => {\n event.res.headers.set('content-type', 'text/html; charset=utf-8');\n return template;\n});\n`;\n}\n
|
|
1
|
+
{"version":3,"file":"renderers.js","names":[],"sources":["../../../../src/lib/nitro/renderers.ts"],"sourcesContent":["/**\n * Code snippet emitted into virtual modules to create a request-scoped\n * fetch using ofetch's `createFetch` + h3's `fetchWithEvent`.\n *\n * Shared between the SSR renderer and page-endpoint virtual modules so\n * the fetch-wiring logic stays in sync.\n *\n * The emitted variable is named `serverFetch` — callers should reference it\n * by that name.\n */\nexport const SERVER_FETCH_FACTORY_SNIPPET = `\n const serverFetch = createFetch({\n fetch: (resource, init) => {\n const url = resource instanceof Request ? resource.url : resource.toString();\n return fetchWithEvent(event, url, init);\n }\n });`;\n\n/**\n * SSR renderer virtual module content.\n *\n * This code runs inside Nitro's server runtime (Node.js context) where\n * event.node is always populated. In h3 v2, event.node is typed as optional,\n * so we use h3's first-class event properties (event.path, event.method) where\n * possible and apply optional chaining when accessing the Node.js context for\n * the Angular renderer which requires raw req/res objects.\n *\n * h3 v2 idiomatic APIs used:\n * - defineHandler (replaces defineEventHandler / eventHandler)\n * - event.path (replaces event.node.req.url)\n * - getResponseHeader compat shim (still available in h3 v2)\n */\nexport function ssrRenderer() {\n return `\nimport { createFetch } from 'ofetch';\nimport { defineHandler, fetchWithEvent } from 'nitro/h3';\n// @ts-ignore\nimport renderer from '#analog/ssr';\nimport template from '#analog/index';\n\nconst normalizeHtmlRequestUrl = (url) =>\n url.replace(/\\\\/index\\\\.html(?=$|[?#])/, '/');\n\nexport default defineHandler(async (event) => {\n event.res.headers.set('content-type', 'text/html; charset=utf-8');\n const noSSR = event.res.headers.get('x-analog-no-ssr');\n const requestPath = normalizeHtmlRequestUrl(event.path);\n\n if (noSSR === 'true') {\n return template;\n }\n\n // event.path is the canonical h3 v2 way to access the request URL.\n // event.node?.req and event.node?.res are needed by the Angular SSR renderer\n // which operates on raw Node.js request/response objects.\n // During prerendering (Nitro v3 fetch-based pipeline), event.node is undefined.\n // The Angular renderer requires a req object with at least { headers, url },\n // so we provide a minimal stub to avoid runtime errors in prerender context.\n const req = event.node?.req\n ? {\n ...event.node.req,\n url: requestPath,\n originalUrl: requestPath,\n }\n : {\n headers: { host: 'localhost' },\n url: requestPath,\n originalUrl: requestPath,\n connection: {},\n };\n const res = event.node?.res;\n${SERVER_FETCH_FACTORY_SNIPPET}\n\n const html = await renderer(requestPath, template, { req, res, fetch: serverFetch });\n\n return html;\n});`;\n}\n\n/**\n * Client-only renderer virtual module content.\n *\n * Used when SSR is disabled — simply serves the static index.html template\n * for every route, letting the client-side Angular router handle navigation.\n */\nexport function clientRenderer() {\n return `\nimport { defineHandler } from 'nitro/h3';\nimport template from '#analog/index';\n\nexport default defineHandler(async (event) => {\n event.res.headers.set('content-type', 'text/html; charset=utf-8');\n return template;\n});\n`;\n}\n"],"mappings":";;;;;;;;;;;AAUA,IAAa,+BAA+B"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
This is an [AnalogJS](https://analogjs.org) app — an Angular meta-framework powered by Vite.
|
|
4
|
+
|
|
5
|
+
Framework conventions for AI assistants — file-based routing, server/API routes, data fetching, content routes, and modern Angular usage — ship with the framework and are installed alongside your dependencies. Read them here:
|
|
6
|
+
|
|
7
|
+
- `node_modules/@analogjs/platform/AGENTS.md`
|
|
8
|
+
|
|
9
|
+
Reading from `@analogjs/platform` keeps the guidance in sync with the installed version instead of drifting from a copy checked into the project.
|
|
10
|
+
|
|
11
|
+
Full documentation: https://analogjs.org/docs · LLM-friendly index: https://analogjs.org/llms.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Strictly follow the rules in @AGENTS.md
|
|
@@ -5,6 +5,7 @@ const require_minimum_supported_versions = require("./versions/minimum-supported
|
|
|
5
5
|
const require_add_angular_app = require("./lib/add-angular-app.js");
|
|
6
6
|
const require_src_generators_init_generator = require("../init/generator.js");
|
|
7
7
|
const require_add_files = require("./lib/add-files.js");
|
|
8
|
+
const require_add_agent_context = require("./lib/add-agent-context.js");
|
|
8
9
|
const require_add_tailwind_config = require("./lib/add-tailwind-config.js");
|
|
9
10
|
const require_cleanup_files = require("./lib/cleanup-files.js");
|
|
10
11
|
const require_add_analog_project_config = require("./lib/add-analog-project-config.js");
|
|
@@ -72,6 +73,7 @@ async function appGenerator(tree, options) {
|
|
|
72
73
|
require_update_index_html.updateIndex(tree, normalizedOptions.analogAppName);
|
|
73
74
|
if (normalizedOptions.addTailwind) await require_add_tailwind_config.addTailwindConfig(tree, normalizedOptions.projectName);
|
|
74
75
|
require_add_home_page.addHomePage(tree, normalizedOptions, majorAngularVersion);
|
|
76
|
+
if (!normalizedOptions.skipAgentContext) require_add_agent_context.addAgentContext(tree, normalizedOptions.projectRoot);
|
|
75
77
|
require_cleanup_files.cleanupFiles(tree, normalizedOptions);
|
|
76
78
|
if (!normalizedOptions.skipFormat) await (0, _nx_devkit.formatFiles)(tree);
|
|
77
79
|
return () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.js","names":[],"sources":["../../../../../../../../nx-plugin/src/generators/app/generator.ts"],"sourcesContent":["import {\n formatFiles,\n getWorkspaceLayout,\n installPackagesTask,\n names,\n offsetFromRoot as determineOffsetFromRoot,\n stripIndents,\n Tree,\n addDependenciesToPackageJson,\n updateJson,\n} from '@nx/devkit';\nimport { wrapAngularDevkitSchematic } from '@nx/devkit/ngcli-adapter';\nimport { AnalogNxApplicationGeneratorOptions } from './schema';\nimport { major, coerce } from 'semver';\nimport { getInstalledPackageVersion } from '../../utils/version-utils';\nimport { addHomePage } from './lib/add-home-page';\nimport { belowMinimumSupportedNxVersion } from './versions/minimum-supported-versions';\nimport { addAngularApp } from './lib/add-angular-app';\nimport setupAnalogGenerator from '../init/generator';\nimport { addFiles } from './lib/add-files';\nimport { addTailwindConfig } from './lib/add-tailwind-config';\nimport { cleanupFiles } from './lib/cleanup-files';\nimport { addAnalogProjectConfig } from './lib/add-analog-project-config';\nimport { updateIndex } from './lib/update-index-html';\n\nexport interface NormalizedOptions\n extends AnalogNxApplicationGeneratorOptions, ReturnType<typeof names> {\n projectName: string;\n projectRoot: string;\n projectDirectory: string;\n parsedTags: string[];\n offsetFromRoot: string;\n appsDir: string;\n nxPackageNamespace: string;\n isNx: boolean;\n}\n\nfunction normalizeOptions(\n tree: Tree,\n options: AnalogNxApplicationGeneratorOptions,\n): NormalizedOptions {\n const isNx = tree.exists('/nx.json');\n const appsDir = isNx ? getWorkspaceLayout(tree).appsDir : 'projects';\n const allNames = names(options.analogAppName);\n const projectDirectory = allNames.fileName;\n const projectName = projectDirectory.replace(new RegExp('/', 'g'), '-');\n const projectRoot = `${appsDir}/${projectDirectory}`;\n const parsedTags = options.tags\n ? options.tags.split(',').map((s) => s.trim())\n : [];\n const offsetFromRoot = determineOffsetFromRoot(projectRoot);\n const nxPackageNamespace = '@nx';\n const addTailwind = options.addTailwind ?? true;\n\n return {\n ...options,\n ...allNames,\n projectName,\n projectRoot,\n projectDirectory,\n parsedTags,\n offsetFromRoot,\n appsDir,\n nxPackageNamespace,\n addTailwind,\n isNx,\n };\n}\n\nasync function appGenerator(\n tree: Tree,\n options: AnalogNxApplicationGeneratorOptions,\n): Promise<() => void> {\n const nxVersion = getInstalledPackageVersion(tree, 'nx');\n\n if (nxVersion && belowMinimumSupportedNxVersion(nxVersion)) {\n throw new Error(\n stripIndents`Nx v17.0.0 or newer is required to install Analog`,\n );\n }\n\n const normalizedOptions = normalizeOptions(tree, options);\n\n if (nxVersion) {\n await addAngularApp(tree, normalizedOptions);\n } else {\n const angularAppSchematic = wrapAngularDevkitSchematic(\n '@schematics/angular',\n 'application',\n );\n await angularAppSchematic(tree, {\n projectRoot: normalizedOptions.projectRoot,\n name: normalizedOptions.analogAppName,\n });\n addAnalogProjectConfig(\n tree,\n normalizedOptions.projectRoot,\n normalizedOptions.projectName,\n normalizedOptions.parsedTags,\n normalizedOptions.name,\n normalizedOptions.appsDir,\n normalizedOptions.nxPackageNamespace,\n );\n }\n\n await setupAnalogGenerator(tree, {\n project: normalizedOptions.analogAppName,\n vitest: true,\n });\n\n const angularVersion = getInstalledPackageVersion(tree, '@angular/core');\n const coercedAngularVersion = coerce(angularVersion ?? '');\n if (!coercedAngularVersion) {\n throw new Error('Could not determine installed Angular version.');\n }\n const majorAngularVersion = major(coercedAngularVersion);\n addFiles(tree, normalizedOptions, majorAngularVersion);\n addDependenciesToPackageJson(\n tree,\n {\n 'front-matter': '^4.0.2',\n marked: '^18.0.0',\n mermaid: '^10.2.4',\n prismjs: '^1.29.0',\n },\n {},\n );\n\n updateJson<{ dependencies: Record<string, string> }>(\n tree,\n '/package.json',\n (json) => {\n json.dependencies['@angular/platform-server'] = `~${angularVersion}`;\n\n return json;\n },\n );\n\n updateIndex(tree, normalizedOptions.analogAppName);\n\n if (normalizedOptions.addTailwind) {\n await addTailwindConfig(tree, normalizedOptions.projectName);\n }\n\n addHomePage(tree, normalizedOptions, majorAngularVersion);\n\n cleanupFiles(tree, normalizedOptions);\n\n if (!normalizedOptions.skipFormat) {\n await formatFiles(tree);\n }\n\n return () => {\n installPackagesTask(tree);\n };\n}\n\nexport default appGenerator;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"generator.js","names":[],"sources":["../../../../../../../../nx-plugin/src/generators/app/generator.ts"],"sourcesContent":["import {\n formatFiles,\n getWorkspaceLayout,\n installPackagesTask,\n names,\n offsetFromRoot as determineOffsetFromRoot,\n stripIndents,\n Tree,\n addDependenciesToPackageJson,\n updateJson,\n} from '@nx/devkit';\nimport { wrapAngularDevkitSchematic } from '@nx/devkit/ngcli-adapter';\nimport { AnalogNxApplicationGeneratorOptions } from './schema';\nimport { major, coerce } from 'semver';\nimport { getInstalledPackageVersion } from '../../utils/version-utils';\nimport { addHomePage } from './lib/add-home-page';\nimport { belowMinimumSupportedNxVersion } from './versions/minimum-supported-versions';\nimport { addAngularApp } from './lib/add-angular-app';\nimport setupAnalogGenerator from '../init/generator';\nimport { addFiles } from './lib/add-files';\nimport { addAgentContext } from './lib/add-agent-context';\nimport { addTailwindConfig } from './lib/add-tailwind-config';\nimport { cleanupFiles } from './lib/cleanup-files';\nimport { addAnalogProjectConfig } from './lib/add-analog-project-config';\nimport { updateIndex } from './lib/update-index-html';\n\nexport interface NormalizedOptions\n extends AnalogNxApplicationGeneratorOptions, ReturnType<typeof names> {\n projectName: string;\n projectRoot: string;\n projectDirectory: string;\n parsedTags: string[];\n offsetFromRoot: string;\n appsDir: string;\n nxPackageNamespace: string;\n isNx: boolean;\n}\n\nfunction normalizeOptions(\n tree: Tree,\n options: AnalogNxApplicationGeneratorOptions,\n): NormalizedOptions {\n const isNx = tree.exists('/nx.json');\n const appsDir = isNx ? getWorkspaceLayout(tree).appsDir : 'projects';\n const allNames = names(options.analogAppName);\n const projectDirectory = allNames.fileName;\n const projectName = projectDirectory.replace(new RegExp('/', 'g'), '-');\n const projectRoot = `${appsDir}/${projectDirectory}`;\n const parsedTags = options.tags\n ? options.tags.split(',').map((s) => s.trim())\n : [];\n const offsetFromRoot = determineOffsetFromRoot(projectRoot);\n const nxPackageNamespace = '@nx';\n const addTailwind = options.addTailwind ?? true;\n\n return {\n ...options,\n ...allNames,\n projectName,\n projectRoot,\n projectDirectory,\n parsedTags,\n offsetFromRoot,\n appsDir,\n nxPackageNamespace,\n addTailwind,\n isNx,\n };\n}\n\nasync function appGenerator(\n tree: Tree,\n options: AnalogNxApplicationGeneratorOptions,\n): Promise<() => void> {\n const nxVersion = getInstalledPackageVersion(tree, 'nx');\n\n if (nxVersion && belowMinimumSupportedNxVersion(nxVersion)) {\n throw new Error(\n stripIndents`Nx v17.0.0 or newer is required to install Analog`,\n );\n }\n\n const normalizedOptions = normalizeOptions(tree, options);\n\n if (nxVersion) {\n await addAngularApp(tree, normalizedOptions);\n } else {\n const angularAppSchematic = wrapAngularDevkitSchematic(\n '@schematics/angular',\n 'application',\n );\n await angularAppSchematic(tree, {\n projectRoot: normalizedOptions.projectRoot,\n name: normalizedOptions.analogAppName,\n });\n addAnalogProjectConfig(\n tree,\n normalizedOptions.projectRoot,\n normalizedOptions.projectName,\n normalizedOptions.parsedTags,\n normalizedOptions.name,\n normalizedOptions.appsDir,\n normalizedOptions.nxPackageNamespace,\n );\n }\n\n await setupAnalogGenerator(tree, {\n project: normalizedOptions.analogAppName,\n vitest: true,\n });\n\n const angularVersion = getInstalledPackageVersion(tree, '@angular/core');\n const coercedAngularVersion = coerce(angularVersion ?? '');\n if (!coercedAngularVersion) {\n throw new Error('Could not determine installed Angular version.');\n }\n const majorAngularVersion = major(coercedAngularVersion);\n addFiles(tree, normalizedOptions, majorAngularVersion);\n addDependenciesToPackageJson(\n tree,\n {\n 'front-matter': '^4.0.2',\n marked: '^18.0.0',\n mermaid: '^10.2.4',\n prismjs: '^1.29.0',\n },\n {},\n );\n\n updateJson<{ dependencies: Record<string, string> }>(\n tree,\n '/package.json',\n (json) => {\n json.dependencies['@angular/platform-server'] = `~${angularVersion}`;\n\n return json;\n },\n );\n\n updateIndex(tree, normalizedOptions.analogAppName);\n\n if (normalizedOptions.addTailwind) {\n await addTailwindConfig(tree, normalizedOptions.projectName);\n }\n\n addHomePage(tree, normalizedOptions, majorAngularVersion);\n\n if (!normalizedOptions.skipAgentContext) {\n addAgentContext(tree, normalizedOptions.projectRoot);\n }\n\n cleanupFiles(tree, normalizedOptions);\n\n if (!normalizedOptions.skipFormat) {\n await formatFiles(tree);\n }\n\n return () => {\n installPackagesTask(tree);\n };\n}\n\nexport default appGenerator;\n"],"mappings":";;;;;;;;;;;;;;;;AAsCA,SAAS,iBACP,MACA,SACmB;CACnB,MAAM,OAAO,KAAK,OAAO,WAAW;CACpC,MAAM,UAAU,QAAA,GAAA,WAAA,oBAA0B,KAAK,CAAC,UAAU;CAC1D,MAAM,YAAA,GAAA,WAAA,OAAiB,QAAQ,cAAc;CAC7C,MAAM,mBAAmB,SAAS;CAClC,MAAM,cAAc,iBAAiB,wBAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,IAAI;CACvE,MAAM,cAAc,GAAG,QAAQ,GAAG;CAClC,MAAM,aAAa,QAAQ,OACvB,QAAQ,KAAK,MAAM,IAAI,CAAC,KAAK,MAAM,EAAE,MAAM,CAAC,GAC5C,EAAE;CACN,MAAM,kBAAA,GAAA,WAAA,gBAAyC,YAAY;CAC3D,MAAM,qBAAqB;CAC3B,MAAM,cAAc,QAAQ,eAAe;AAE3C,QAAO;EACL,GAAG;EACH,GAAG;EACH;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;AAGH,eAAe,aACb,MACA,SACqB;CACrB,MAAM,YAAY,sBAAA,2BAA2B,MAAM,KAAK;AAExD,KAAI,aAAa,mCAAA,+BAA+B,UAAU,CACxD,OAAM,IAAI,MACR,WAAA,YAAY,oDACb;CAGH,MAAM,oBAAoB,iBAAiB,MAAM,QAAQ;AAEzD,KAAI,UACF,OAAM,wBAAA,cAAc,MAAM,kBAAkB;MACvC;AAKL,SAAA,GAAA,yBAAA,4BAHE,uBACA,cACD,CACyB,MAAM;GAC9B,aAAa,kBAAkB;GAC/B,MAAM,kBAAkB;GACzB,CAAC;AACF,oCAAA,uBACE,MACA,kBAAkB,aAClB,kBAAkB,aAClB,kBAAkB,YAClB,kBAAkB,MAClB,kBAAkB,SAClB,kBAAkB,mBACnB;;AAGH,OAAM,sCAAqB,MAAM;EAC/B,SAAS,kBAAkB;EAC3B,QAAQ;EACT,CAAC;CAEF,MAAM,iBAAiB,sBAAA,2BAA2B,MAAM,gBAAgB;CACxE,MAAM,yBAAA,GAAA,OAAA,QAA+B,kBAAkB,GAAG;AAC1D,KAAI,CAAC,sBACH,OAAM,IAAI,MAAM,iDAAiD;CAEnE,MAAM,uBAAA,GAAA,OAAA,OAA4B,sBAAsB;AACxD,mBAAA,SAAS,MAAM,mBAAmB,oBAAoB;AACtD,EAAA,GAAA,WAAA,8BACE,MACA;EACE,gBAAgB;EAChB,QAAQ;EACR,SAAS;EACT,SAAS;EACV,EACD,EAAE,CACH;AAED,EAAA,GAAA,WAAA,YACE,MACA,kBACC,SAAS;AACR,OAAK,aAAa,8BAA8B,IAAI;AAEpD,SAAO;GAEV;AAED,2BAAA,YAAY,MAAM,kBAAkB,cAAc;AAElD,KAAI,kBAAkB,YACpB,OAAM,4BAAA,kBAAkB,MAAM,kBAAkB,YAAY;AAG9D,uBAAA,YAAY,MAAM,mBAAmB,oBAAoB;AAEzD,KAAI,CAAC,kBAAkB,iBACrB,2BAAA,gBAAgB,MAAM,kBAAkB,YAAY;AAGtD,uBAAA,aAAa,MAAM,kBAAkB;AAErC,KAAI,CAAC,kBAAkB,WACrB,QAAA,GAAA,WAAA,aAAkB,KAAK;AAGzB,cAAa;AACX,GAAA,GAAA,WAAA,qBAAoB,KAAK"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require("../../../../_virtual/_rolldown/runtime.js");
|
|
2
|
+
let node_path = require("node:path");
|
|
3
|
+
let node_fs = require("node:fs");
|
|
4
|
+
//#region packages/nx-plugin/src/generators/app/lib/add-agent-context.ts
|
|
5
|
+
var AGENT_CONTEXT_FILES = ["AGENTS.md", "CLAUDE.md"];
|
|
6
|
+
function addAgentContext(tree, projectRoot) {
|
|
7
|
+
for (const fileName of AGENT_CONTEXT_FILES) {
|
|
8
|
+
const filePath = `${projectRoot}/${fileName}`;
|
|
9
|
+
if (tree.exists(filePath)) continue;
|
|
10
|
+
tree.write(filePath, (0, node_fs.readFileSync)((0, node_path.join)(__dirname, "..", "files", "agents", fileName), "utf-8"));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
exports.addAgentContext = addAgentContext;
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=add-agent-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-agent-context.js","names":[],"sources":["../../../../../../../../../nx-plugin/src/generators/app/lib/add-agent-context.ts"],"sourcesContent":["import { Tree } from '@nx/devkit';\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nconst AGENT_CONTEXT_FILES = ['AGENTS.md', 'CLAUDE.md'];\n\n// Seeds agent context in the app so AI coding assistants pick up Analog\n// conventions (see node_modules/@analogjs/platform/AGENTS.md).\nexport function addAgentContext(tree: Tree, projectRoot: string) {\n for (const fileName of AGENT_CONTEXT_FILES) {\n const filePath = `${projectRoot}/${fileName}`;\n\n if (tree.exists(filePath)) {\n continue;\n }\n\n tree.write(\n filePath,\n readFileSync(join(__dirname, '..', 'files', 'agents', fileName), 'utf-8'),\n );\n }\n}\n"],"mappings":";;;;AAIA,IAAM,sBAAsB,CAAC,aAAa,YAAY;AAItD,SAAgB,gBAAgB,MAAY,aAAqB;AAC/D,MAAK,MAAM,YAAY,qBAAqB;EAC1C,MAAM,WAAW,GAAG,YAAY,GAAG;AAEnC,MAAI,KAAK,OAAO,SAAS,CACvB;AAGF,OAAK,MACH,WAAA,GAAA,QAAA,eAAA,GAAA,UAAA,MACkB,WAAW,MAAM,SAAS,UAAU,SAAS,EAAE,QAAQ,CAC1E"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export declare const V18_X_NX_DEVKIT = "^20.0.0";
|
|
2
2
|
export declare const V18_X_NX_ANGULAR = "^20.0.0";
|
|
3
|
-
export declare const V18_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.
|
|
4
|
-
export declare const V18_X_ANALOG_JS_ROUTER = "^3.0.0-alpha.
|
|
5
|
-
export declare const V18_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.
|
|
6
|
-
export declare const V18_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.
|
|
3
|
+
export declare const V18_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.66";
|
|
4
|
+
export declare const V18_X_ANALOG_JS_ROUTER = "^3.0.0-alpha.66";
|
|
5
|
+
export declare const V18_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.66";
|
|
6
|
+
export declare const V18_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.66";
|
|
7
7
|
export declare const V18_X_FRONT_MATTER = "^4.0.2";
|
|
8
8
|
export declare const V18_X_MARKED = "^18.0.0";
|
|
9
9
|
export declare const V18_X_MARKED_GFM_HEADING_ID = "^4.1.1";
|
|
@@ -15,7 +15,7 @@ export declare const V18_X_TAILWINDCSS = "^4.2.2";
|
|
|
15
15
|
export declare const V18_X_TAILWINDCSS_POSTCSS = "^4.2.2";
|
|
16
16
|
export declare const V18_X_TAILWINDCSS_VITE = "^4.2.2";
|
|
17
17
|
export declare const V18_X_POSTCSS = "^8.5.6";
|
|
18
|
-
export declare const V18_X_ANALOG_JS_PLATFORM = "^3.0.0-alpha.
|
|
18
|
+
export declare const V18_X_ANALOG_JS_PLATFORM = "^3.0.0-alpha.66";
|
|
19
19
|
export declare const V18_X_ANGULAR_DEVKIT_BUILD_ANGULAR = "^19.0.0";
|
|
20
20
|
export declare const V18_X_NX_VITE = "^21.0.0";
|
|
21
21
|
export declare const V18_X_NX_LINTER = "^21.0.0";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"versions.js","names":[],"sources":["../../../../../../../../../../nx-plugin/src/generators/app/versions/nx_18_X/versions.ts"],"sourcesContent":["// V18_X\n// dependencies\nexport const V18_X_NX_DEVKIT = '^20.0.0';\nexport const V18_X_NX_ANGULAR = '^20.0.0';\nexport const V18_X_ANALOG_JS_CONTENT = '^3.0.0-alpha.
|
|
1
|
+
{"version":3,"file":"versions.js","names":[],"sources":["../../../../../../../../../../nx-plugin/src/generators/app/versions/nx_18_X/versions.ts"],"sourcesContent":["// V18_X\n// dependencies\nexport const V18_X_NX_DEVKIT = '^20.0.0';\nexport const V18_X_NX_ANGULAR = '^20.0.0';\nexport const V18_X_ANALOG_JS_CONTENT = '^3.0.0-alpha.66';\nexport const V18_X_ANALOG_JS_ROUTER = '^3.0.0-alpha.66';\nexport const V18_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = '^3.0.0-alpha.66';\nexport const V18_X_ANALOG_JS_VITEST_ANGULAR = '^3.0.0-alpha.66';\nexport const V18_X_FRONT_MATTER = '^4.0.2';\nexport const V18_X_MARKED = '^18.0.0';\nexport const V18_X_MARKED_GFM_HEADING_ID = '^4.1.1';\nexport const V18_X_MARKED_HIGHLIGHT = '^2.2.1';\nexport const V18_X_MARKED_MANGLE = '^1.1.10';\nexport const V18_X_MERMAID = '^10.2.4';\nexport const V18_X_PRISMJS = '^1.29.0';\nexport const V18_X_TAILWINDCSS = '^4.2.2';\nexport const V18_X_TAILWINDCSS_POSTCSS = '^4.2.2';\nexport const V18_X_TAILWINDCSS_VITE = '^4.2.2';\nexport const V18_X_POSTCSS = '^8.5.6';\n\n// devDependencies\nexport const V18_X_ANALOG_JS_PLATFORM = '^3.0.0-alpha.66';\nexport const V18_X_ANGULAR_DEVKIT_BUILD_ANGULAR = '^19.0.0';\nexport const V18_X_NX_VITE = '^21.0.0';\nexport const V18_X_NX_LINTER = '^21.0.0';\nexport const V18_X_JSDOM = '^22.1.0';\nexport const V18_X_VITE = '^8.0.0';\nexport const V18_X_VITE_TSCONFIG_PATHS = '^4.2.0';\nexport const V18_X_VITEST = '^4.0.0';\nexport const V18_X_ZOD = '^3.21.4';\n"],"mappings":";AAeA,IAAa,oBAAoB;AACjC,IAAa,4BAA4B;AACzC,IAAa,yBAAyB;AACtC,IAAa,gBAAgB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require("../../../_virtual/_rolldown/runtime.js");
|
|
2
2
|
let _nx_devkit = require("@nx/devkit");
|
|
3
|
+
let node_path = require("node:path");
|
|
3
4
|
//#region packages/nx-plugin/src/generators/preset/generator.ts
|
|
4
5
|
async function generator_default(tree, options) {
|
|
5
6
|
(0, _nx_devkit.ensurePackage)("@nx/angular", _nx_devkit.NX_VERSION);
|
|
@@ -8,7 +9,11 @@ async function generator_default(tree, options) {
|
|
|
8
9
|
(0, _nx_devkit.ensurePackage)("@angular-devkit/core", "latest");
|
|
9
10
|
(0, _nx_devkit.ensurePackage)("rxjs", "latest");
|
|
10
11
|
const generatorModule = await Promise.resolve().then(() => require("../app/generator.js"));
|
|
11
|
-
const installTask = await ("default" in generatorModule ? generatorModule.default : generatorModule)(tree,
|
|
12
|
+
const installTask = await ("default" in generatorModule ? generatorModule.default : generatorModule)(tree, {
|
|
13
|
+
...options,
|
|
14
|
+
skipAgentContext: true
|
|
15
|
+
});
|
|
16
|
+
(0, _nx_devkit.generateFiles)(tree, (0, node_path.join)(__dirname, "..", "app", "files", "agents"), ".", options);
|
|
12
17
|
if (tree.exists("/tsconfig.base.json")) (0, _nx_devkit.updateJson)(tree, "/tsconfig.base.json", (json) => {
|
|
13
18
|
delete json.compilerOptions?.baseUrl;
|
|
14
19
|
return json;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.js","names":[],"sources":["../../../../../../../../nx-plugin/src/generators/preset/generator.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"generator.js","names":[],"sources":["../../../../../../../../nx-plugin/src/generators/preset/generator.ts"],"sourcesContent":["import {\n ensurePackage,\n generateFiles,\n NX_VERSION,\n Tree,\n updateJson,\n} from '@nx/devkit';\nimport { join } from 'node:path';\nimport { PresetGeneratorSchema } from './schema';\n\nexport default async function (\n tree: Tree,\n options: PresetGeneratorSchema,\n): Promise<() => void> {\n ensurePackage('@nx/angular', NX_VERSION);\n ensurePackage('@nx/vite', NX_VERSION);\n ensurePackage('@nx/eslint', NX_VERSION);\n ensurePackage('@angular-devkit/core', 'latest');\n ensurePackage('rxjs', 'latest');\n\n const generatorModule = await import('../app/generator');\n const appGenerator =\n 'default' in generatorModule ? generatorModule.default : generatorModule;\n\n const installTask = await appGenerator(tree, {\n ...options,\n skipAgentContext: true,\n });\n\n // Seed agent context at the workspace root so AI coding assistants pick up\n // Analog conventions (see node_modules/@analogjs/platform/AGENTS.md).\n generateFiles(\n tree,\n join(__dirname, '..', 'app', 'files', 'agents'),\n '.',\n options,\n );\n\n if (tree.exists('/tsconfig.base.json')) {\n updateJson<{\n compilerOptions?: {\n baseUrl?: string;\n };\n }>(tree, '/tsconfig.base.json', (json) => {\n delete json.compilerOptions?.baseUrl;\n return json;\n });\n }\n\n return installTask;\n}\n"],"mappings":";;;;AAUA,eAAA,kBACE,MACA,SACqB;AACrB,EAAA,GAAA,WAAA,eAAc,eAAe,WAAA,WAAW;AACxC,EAAA,GAAA,WAAA,eAAc,YAAY,WAAA,WAAW;AACrC,EAAA,GAAA,WAAA,eAAc,cAAc,WAAA,WAAW;AACvC,EAAA,GAAA,WAAA,eAAc,wBAAwB,SAAS;AAC/C,EAAA,GAAA,WAAA,eAAc,QAAQ,SAAS;CAE/B,MAAM,kBAAkB,MAAA,QAAA,SAAA,CAAA,WAAA,QAAM,sBAAA,CAAA;CAI9B,MAAM,cAAc,OAFlB,aAAa,kBAAkB,gBAAgB,UAAU,iBAEpB,MAAM;EAC3C,GAAG;EACH,kBAAkB;EACnB,CAAC;AAIF,EAAA,GAAA,WAAA,eACE,OAAA,GAAA,UAAA,MACK,WAAW,MAAM,OAAO,SAAS,SAAS,EAC/C,KACA,QACD;AAED,KAAI,KAAK,OAAO,sBAAsB,CACpC,EAAA,GAAA,WAAA,YAIG,MAAM,wBAAwB,SAAS;AACxC,SAAO,KAAK,iBAAiB;AAC7B,SAAO;GACP;AAGJ,QAAO"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export declare const V19_X_ANALOG_JS_ROUTER = "^3.0.0-alpha.
|
|
2
|
-
export declare const V19_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.
|
|
1
|
+
export declare const V19_X_ANALOG_JS_ROUTER = "^3.0.0-alpha.66";
|
|
2
|
+
export declare const V19_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.66";
|
|
3
3
|
export declare const V19_X_MARKED = "^18.0.0";
|
|
4
4
|
export declare const V19_X_MARKED_GFM_HEADING_ID = "^4.1.1";
|
|
5
5
|
export declare const V19_X_MARKED_HIGHLIGHT = "^2.2.1";
|
|
6
6
|
export declare const V19_X_MARKED_MANGLE = "^1.1.10";
|
|
7
7
|
export declare const V19_X_PRISMJS = "^1.29.0";
|
|
8
|
-
export declare const V19_X_ANALOG_JS_PLATFORM = "^3.0.0-alpha.
|
|
9
|
-
export declare const V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.
|
|
10
|
-
export declare const V19_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.
|
|
8
|
+
export declare const V19_X_ANALOG_JS_PLATFORM = "^3.0.0-alpha.66";
|
|
9
|
+
export declare const V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.66";
|
|
10
|
+
export declare const V19_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.66";
|
|
11
11
|
export declare const V19_X_NX_ANGULAR = "^22.0.0";
|
|
12
12
|
export declare const V19_X_NX_VITE = "^22.0.0";
|
|
13
13
|
export declare const V19_X_JSDOM = "^22.0.0";
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
//#region packages/nx-plugin/src/utils/versions/ng_19_X/versions.ts
|
|
2
|
-
var V19_X_ANALOG_JS_ROUTER = "^3.0.0-alpha.
|
|
3
|
-
var V19_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.
|
|
2
|
+
var V19_X_ANALOG_JS_ROUTER = "^3.0.0-alpha.66";
|
|
3
|
+
var V19_X_ANALOG_JS_CONTENT = "^3.0.0-alpha.66";
|
|
4
4
|
var V19_X_MARKED = "^18.0.0";
|
|
5
5
|
var V19_X_MARKED_GFM_HEADING_ID = "^4.1.1";
|
|
6
6
|
var V19_X_MARKED_HIGHLIGHT = "^2.2.1";
|
|
7
7
|
var V19_X_MARKED_MANGLE = "^1.1.10";
|
|
8
8
|
var V19_X_PRISMJS = "^1.29.0";
|
|
9
|
-
var V19_X_ANALOG_JS_PLATFORM = "^3.0.0-alpha.
|
|
10
|
-
var V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.
|
|
11
|
-
var V19_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.
|
|
9
|
+
var V19_X_ANALOG_JS_PLATFORM = "^3.0.0-alpha.66";
|
|
10
|
+
var V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = "^3.0.0-alpha.66";
|
|
11
|
+
var V19_X_ANALOG_JS_VITEST_ANGULAR = "^3.0.0-alpha.66";
|
|
12
12
|
var V19_X_NX_VITE = "^22.0.0";
|
|
13
13
|
var V19_X_JSDOM = "^22.0.0";
|
|
14
14
|
var V19_X_VITE_TSCONFIG_PATHS = "^4.2.0";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"versions.js","names":[],"sources":["../../../../../../../../../nx-plugin/src/utils/versions/ng_19_X/versions.ts"],"sourcesContent":["// V19_X\nexport const V19_X_ANALOG_JS_ROUTER = '^3.0.0-alpha.
|
|
1
|
+
{"version":3,"file":"versions.js","names":[],"sources":["../../../../../../../../../nx-plugin/src/utils/versions/ng_19_X/versions.ts"],"sourcesContent":["// V19_X\nexport const V19_X_ANALOG_JS_ROUTER = '^3.0.0-alpha.66';\nexport const V19_X_ANALOG_JS_CONTENT = '^3.0.0-alpha.66';\nexport const V19_X_MARKED = '^18.0.0';\nexport const V19_X_MARKED_GFM_HEADING_ID = '^4.1.1';\nexport const V19_X_MARKED_HIGHLIGHT = '^2.2.1';\nexport const V19_X_MARKED_MANGLE = '^1.1.10';\nexport const V19_X_PRISMJS = '^1.29.0';\n\n// devDependencies\nexport const V19_X_ANALOG_JS_PLATFORM = '^3.0.0-alpha.66';\nexport const V19_X_ANALOG_JS_VITE_PLUGIN_ANGULAR = '^3.0.0-alpha.66';\nexport const V19_X_ANALOG_JS_VITEST_ANGULAR = '^3.0.0-alpha.66';\nexport const V19_X_NX_ANGULAR = '^22.0.0';\nexport const V19_X_NX_VITE = '^22.0.0';\nexport const V19_X_JSDOM = '^22.0.0';\nexport const V19_X_VITE_TSCONFIG_PATHS = '^4.2.0';\nexport const V19_X_VITEST = '^3.0.0';\nexport const V19_X_VITE = '^6.0.0';\nexport const NX_X_LATEST_VITE = '^8.0.0';\nexport const NX_X_LATEST_VITEST = '^4.0.0';\nexport const NX_X_LATEST_NX = '^23.0.0';\n"],"mappings":";AACA,IAAa,yBAAyB;AACtC,IAAa,0BAA0B;AACvC,IAAa,eAAe;AAC5B,IAAa,8BAA8B;AAC3C,IAAa,yBAAyB;AACtC,IAAa,sBAAsB;AACnC,IAAa,gBAAgB;AAG7B,IAAa,2BAA2B;AACxC,IAAa,sCAAsC;AACnD,IAAa,iCAAiC;AAE9C,IAAa,gBAAgB;AAC7B,IAAa,cAAc;AAC3B,IAAa,4BAA4B;AACzC,IAAa,eAAe;AAC5B,IAAa,aAAa;AAC1B,IAAa,mBAAmB;AAChC,IAAa,qBAAqB;AAClC,IAAa,iBAAiB"}
|
package/src/lib/options.d.ts
CHANGED
|
@@ -6,9 +6,15 @@ import type { StylePipelineOptions } from "./style-pipeline.js";
|
|
|
6
6
|
declare module "nitro/types" {
|
|
7
7
|
interface NitroRouteConfig {
|
|
8
8
|
ssr?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Disable progressive streaming SSR for matching routes (falls back to a
|
|
11
|
+
* buffered render). Only meaningful when `experimental.streaming` is on.
|
|
12
|
+
*/
|
|
13
|
+
streaming?: boolean;
|
|
9
14
|
}
|
|
10
15
|
interface NitroRouteRules {
|
|
11
16
|
ssr?: boolean;
|
|
17
|
+
streaming?: boolean;
|
|
12
18
|
}
|
|
13
19
|
}
|
|
14
20
|
export interface PrerenderOptions {
|
|
@@ -10,6 +10,8 @@ import { routeGenerationPlugin } from "./route-generation-plugin.js";
|
|
|
10
10
|
import { resolveStylePipelinePlugins } from "./style-pipeline.js";
|
|
11
11
|
import { i18nComponentRegistryPlugin } from "./i18n-component-registry-plugin.js";
|
|
12
12
|
import { analogNitroPlugin } from "./nitro/analog-nitro-plugin.js";
|
|
13
|
+
import "node:module";
|
|
14
|
+
import "node:path";
|
|
13
15
|
//#region packages/platform/src/lib/platform-plugin.ts
|
|
14
16
|
function platformPlugin(opts = {}) {
|
|
15
17
|
applyDebugOption(opts.debug, opts.workspaceRoot);
|
|
@@ -38,7 +40,7 @@ function platformPlugin(opts = {}) {
|
|
|
38
40
|
...contentPlugin(platformOptions?.content, platformOptions),
|
|
39
41
|
...platformOptions.i18n ? [i18nComponentRegistryPlugin()] : [],
|
|
40
42
|
...serverModePlugin(),
|
|
41
|
-
|
|
43
|
+
clearClientPageEndpointsPlugin()
|
|
42
44
|
];
|
|
43
45
|
}
|
|
44
46
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-plugin.js","names":[],"sources":["../../../src/lib/platform-plugin.ts"],"sourcesContent":["import { Plugin } from 'vite';\n\nimport { Options } from './options.js';\nimport {\n activateDeferredDebug,\n applyDebugOption,\n debugPlatform,\n} from './utils/debug.js';\nimport { routerPlugin } from './router-plugin.js';\nimport { ssrBuildPlugin } from './ssr/ssr-build-plugin.js';\nimport { contentPlugin } from './content-plugin.js';\nimport { clearClientPageEndpointsPlugin } from './clear-client-page-endpoint.js';\nimport { depsPlugin } from './deps-plugin.js';\nimport { injectHTMLPlugin } from './ssr/inject-html-plugin.js';\nimport { serverModePlugin } from '../server-mode-plugin.js';\nimport { routeGenerationPlugin } from './route-generation-plugin.js';\nimport { resolveStylePipelinePlugins } from './style-pipeline.js';\nimport { i18nComponentRegistryPlugin } from './i18n-component-registry-plugin.js';\nimport { analogNitroPlugin } from './nitro/analog-nitro-plugin.js';\n\nexport function platformPlugin(opts: Options = {}): Plugin[] {\n applyDebugOption(opts.debug, opts.workspaceRoot);\n\n const isTest = process.env['NODE_ENV'] === 'test' || !!process.env['VITEST'];\n const { ...platformOptions } = {\n ssr: true,\n ...opts,\n };\n\n debugPlatform('experimental options resolved', {\n typedRouter: platformOptions.experimental?.typedRouter,\n stylePipeline: !!platformOptions.experimental?.stylePipeline,\n });\n\n return [\n {\n name: 'analogjs-debug-activate',\n config(_, { command }) {\n activateDeferredDebug(command);\n },\n },\n analogNitroPlugin(platformOptions),\n ...(platformOptions.ssr\n ? [...ssrBuildPlugin(), ...injectHTMLPlugin()]\n : []),\n ...(!isTest ? depsPlugin(platformOptions) : []),\n ...resolveStylePipelinePlugins(\n platformOptions.experimental?.stylePipeline,\n platformOptions.workspaceRoot,\n ),\n ...routerPlugin(platformOptions),\n routeGenerationPlugin(platformOptions),\n ...contentPlugin(platformOptions?.content, platformOptions),\n ...(platformOptions.i18n ? [i18nComponentRegistryPlugin()] : []),\n ...serverModePlugin(),\n
|
|
1
|
+
{"version":3,"file":"platform-plugin.js","names":[],"sources":["../../../src/lib/platform-plugin.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { join } from 'node:path';\nimport { Plugin } from 'vite';\n\nimport { Options } from './options.js';\nimport {\n activateDeferredDebug,\n applyDebugOption,\n debugPlatform,\n} from './utils/debug.js';\nimport { routerPlugin } from './router-plugin.js';\nimport { ssrBuildPlugin } from './ssr/ssr-build-plugin.js';\nimport { contentPlugin } from './content-plugin.js';\nimport { clearClientPageEndpointsPlugin } from './clear-client-page-endpoint.js';\nimport { depsPlugin } from './deps-plugin.js';\nimport { injectHTMLPlugin } from './ssr/inject-html-plugin.js';\nimport { serverModePlugin } from '../server-mode-plugin.js';\nimport { routeGenerationPlugin } from './route-generation-plugin.js';\nimport { resolveStylePipelinePlugins } from './style-pipeline.js';\nimport { i18nComponentRegistryPlugin } from './i18n-component-registry-plugin.js';\nimport { analogNitroPlugin } from './nitro/analog-nitro-plugin.js';\n\n/**\n * The installed `@angular/core` major version, resolved from the workspace root\n * (where the app's Angular is installed). Returns `null` when it cannot be\n * detected, in which case streaming is not blocked and the build-time\n * anchor-drift detection is relied on instead.\n */\nfunction getAngularCoreMajor(workspaceRoot: string): number | null {\n try {\n const req = createRequire(join(workspaceRoot, 'noop.js'));\n const { version } = req('@angular/core/package.json') as {\n version: string;\n };\n const major = Number.parseInt(version.split('.')[0], 10);\n return Number.isNaN(major) ? null : major;\n } catch {\n return null;\n }\n}\n\nexport function platformPlugin(opts: Options = {}): Plugin[] {\n applyDebugOption(opts.debug, opts.workspaceRoot);\n\n const isTest = process.env['NODE_ENV'] === 'test' || !!process.env['VITEST'];\n const { ...platformOptions } = {\n ssr: true,\n ...opts,\n };\n\n debugPlatform('experimental options resolved', {\n typedRouter: platformOptions.experimental?.typedRouter,\n stylePipeline: !!platformOptions.experimental?.stylePipeline,\n });\n\n return [\n {\n name: 'analogjs-debug-activate',\n config(_, { command }) {\n activateDeferredDebug(command);\n },\n },\n analogNitroPlugin(platformOptions),\n ...(platformOptions.ssr\n ? [...ssrBuildPlugin(), ...injectHTMLPlugin()]\n : []),\n ...(!isTest ? depsPlugin(platformOptions) : []),\n ...resolveStylePipelinePlugins(\n platformOptions.experimental?.stylePipeline,\n platformOptions.workspaceRoot,\n ),\n ...routerPlugin(platformOptions),\n routeGenerationPlugin(platformOptions),\n ...contentPlugin(platformOptions?.content, platformOptions),\n ...(platformOptions.i18n ? [i18nComponentRegistryPlugin()] : []),\n ...serverModePlugin(),\n clearClientPageEndpointsPlugin(),\n ];\n}\n"],"mappings":";;;;;;;;;;;;;;;AAyCA,SAAgB,eAAe,OAAgB,EAAE,EAAY;AAC3D,kBAAiB,KAAK,OAAO,KAAK,cAAc;CAEhD,MAAM,SAAA,QAAA,IAAA,aAAqC,UAAU,CAAC,CAAC,QAAQ,IAAI;CACnE,MAAM,EAAE,GAAG,oBAAoB;EAC7B,KAAK;EACL,GAAG;EACJ;AAED,eAAc,iCAAiC;EAC7C,aAAa,gBAAgB,cAAc;EAC3C,eAAe,CAAC,CAAC,gBAAgB,cAAc;EAChD,CAAC;AAEF,QAAO;EACL;GACE,MAAM;GACN,OAAO,GAAG,EAAE,WAAW;AACrB,0BAAsB,QAAQ;;GAEjC;EACD,kBAAkB,gBAAgB;EAClC,GAAI,gBAAgB,MAChB,CAAC,GAAG,gBAAgB,EAAE,GAAG,kBAAkB,CAAC,GAC5C,EAAE;EACN,GAAI,CAAC,SAAS,WAAW,gBAAgB,GAAG,EAAE;EAC9C,GAAG,4BACD,gBAAgB,cAAc,eAC9B,gBAAgB,cACjB;EACD,GAAG,aAAa,gBAAgB;EAChC,sBAAsB,gBAAgB;EACtC,GAAG,cAAc,iBAAiB,SAAS,gBAAgB;EAC3D,GAAI,gBAAgB,OAAO,CAAC,6BAA6B,CAAC,GAAG,EAAE;EAC/D,GAAG,kBAAkB;EACrB,gCAAgC;EACjC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-build scrub + proxy generation for server functions.
|
|
3
|
+
*
|
|
4
|
+
* On the client build a `*.server.ts` module must not carry server handler
|
|
5
|
+
* bodies (which `inject()` server services, read the request, hit the database).
|
|
6
|
+
* But the client still imports the named `serverFn` exports to dispatch them.
|
|
7
|
+
* This transform regenerates the module so each `export const NAME = serverFn(
|
|
8
|
+
* config, handler)` becomes a call to the client-safe `createServerFnRef`
|
|
9
|
+
* factory — keeping the `{ id, url, method }` metadata `injectServerFn` needs,
|
|
10
|
+
* dropping the handler and every server-only import. Because the regenerated
|
|
11
|
+
* module references nothing else, the server code tree-shakes away entirely.
|
|
12
|
+
*
|
|
13
|
+
* Returns the new module source, or `null` when the module defines no server
|
|
14
|
+
* function (the caller falls back to the page-endpoint scrub for those).
|
|
15
|
+
*/
|
|
16
|
+
export interface ServerFnProxy {
|
|
17
|
+
name: string;
|
|
18
|
+
id: string;
|
|
19
|
+
method: "GET" | "POST";
|
|
20
|
+
}
|
|
21
|
+
export interface ScrubResult {
|
|
22
|
+
code: string;
|
|
23
|
+
proxies: ServerFnProxy[];
|
|
24
|
+
hadDefaultExport: boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare function scrubServerFnModule(code: string, fileId?: string): ScrubResult | null;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { parseSync } from "oxc-parser";
|
|
2
|
+
import { deriveServerFnId } from "@analogjs/vite-plugin-nitro/server-fn-id";
|
|
3
|
+
//#region packages/platform/src/lib/server-fn-client-transform.ts
|
|
4
|
+
var SERVER_FN_SOURCE = "@analogjs/router/server";
|
|
5
|
+
function scrubServerFnModule(code, fileId = "file.ts") {
|
|
6
|
+
if (!code.includes("serverFn")) return null;
|
|
7
|
+
const { program } = parseSync(fileId, code);
|
|
8
|
+
const body = program.body;
|
|
9
|
+
const serverFnLocalNames = collectServerFnLocalNames(body);
|
|
10
|
+
const hadDefaultExport = body.some((node) => node.type === "ExportDefaultDeclaration");
|
|
11
|
+
const proxies = [];
|
|
12
|
+
for (const node of body) {
|
|
13
|
+
if (node.type !== "ExportNamedDeclaration" || node.declaration?.type !== "VariableDeclaration") continue;
|
|
14
|
+
for (const declarator of node.declaration.declarations) {
|
|
15
|
+
if (declarator.id?.type !== "Identifier" || declarator.init?.type !== "CallExpression" || declarator.init.callee?.type !== "Identifier" || !serverFnLocalNames.has(declarator.init.callee.name)) continue;
|
|
16
|
+
proxies.push(toProxy(declarator.id.name, declarator.init.arguments ?? [], fileId));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const callCount = countServerFnCalls(program, serverFnLocalNames);
|
|
20
|
+
if (callCount > proxies.length) throw new Error(`[analog] ${fileId}: found ${callCount} serverFn call(s) but could only rewrite ${proxies.length} for the client build. Server functions must be declared as a directly exported const — \`export const name = serverFn(…)\` — so the handler can be stripped from the browser bundle.`);
|
|
21
|
+
if (proxies.length === 0) return null;
|
|
22
|
+
const lines = [
|
|
23
|
+
`import { createServerFnRef } from '@analogjs/router';`,
|
|
24
|
+
"",
|
|
25
|
+
...proxies.map((p) => `export const ${p.name} = createServerFnRef({ id: ${JSON.stringify(p.id)}, method: ${JSON.stringify(p.method)} });`)
|
|
26
|
+
];
|
|
27
|
+
if (hadDefaultExport) lines.push("", "export default undefined;");
|
|
28
|
+
return {
|
|
29
|
+
code: lines.join("\n") + "\n",
|
|
30
|
+
proxies,
|
|
31
|
+
hadDefaultExport
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Local identifier(s) `serverFn` is imported under from `@analogjs/router/server`. */
|
|
35
|
+
function collectServerFnLocalNames(body) {
|
|
36
|
+
const names = /* @__PURE__ */ new Set();
|
|
37
|
+
for (const node of body) {
|
|
38
|
+
if (node.type !== "ImportDeclaration" || node.source?.value !== SERVER_FN_SOURCE) continue;
|
|
39
|
+
for (const spec of node.specifiers ?? []) if (spec.type === "ImportSpecifier" && spec.imported?.name === "serverFn") names.add(spec.local.name);
|
|
40
|
+
}
|
|
41
|
+
if (names.size === 0) names.add("serverFn");
|
|
42
|
+
return names;
|
|
43
|
+
}
|
|
44
|
+
function toProxy(name, args, fileId) {
|
|
45
|
+
const method = methodForCall(name, args, fileId);
|
|
46
|
+
return {
|
|
47
|
+
name,
|
|
48
|
+
id: deriveServerFnId(fileId, name),
|
|
49
|
+
method
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the HTTP method from the call shape, matching the runtime overloads:
|
|
54
|
+
* `serverFn(handler)` ⇒ GET, `serverFn(schema, handler)` ⇒ POST,
|
|
55
|
+
* `serverFn(config, handler)` ⇒ config-driven.
|
|
56
|
+
*/
|
|
57
|
+
function methodForCall(name, args, fileId) {
|
|
58
|
+
const arg0 = args[0];
|
|
59
|
+
if (isFunctionNode(arg0)) return "GET";
|
|
60
|
+
if (arg0?.type === "ObjectExpression") {
|
|
61
|
+
let method;
|
|
62
|
+
let hasInput = false;
|
|
63
|
+
for (const prop of arg0.properties ?? []) {
|
|
64
|
+
if (prop.type !== "Property") continue;
|
|
65
|
+
const key = prop.key?.type === "Identifier" ? prop.key.name : prop.key?.value;
|
|
66
|
+
if (key === "method" && isStringLiteral(prop.value)) method = prop.value.value;
|
|
67
|
+
else if (key === "input") hasInput = true;
|
|
68
|
+
}
|
|
69
|
+
if (method === "GET" && hasInput) throw new Error(`[analog] serverFn "${name}" in ${fileId} declares method: 'GET' with an input schema; GET carries no body. Use POST or drop the input.`);
|
|
70
|
+
return method ?? (hasInput ? "POST" : "GET");
|
|
71
|
+
}
|
|
72
|
+
if (arg0 && args.length >= 2) return "POST";
|
|
73
|
+
throw new Error(`[analog] serverFn "${name}" in ${fileId} must be called as serverFn(handler), serverFn(schema, handler), or serverFn(config, handler).`);
|
|
74
|
+
}
|
|
75
|
+
function isFunctionNode(node) {
|
|
76
|
+
return node?.type === "ArrowFunctionExpression" || node?.type === "FunctionExpression";
|
|
77
|
+
}
|
|
78
|
+
function isStringLiteral(node) {
|
|
79
|
+
return node?.type === "Literal" && typeof node.value === "string";
|
|
80
|
+
}
|
|
81
|
+
/** Every `serverFn(...)` call anywhere in the module, not just exported ones. */
|
|
82
|
+
function countServerFnCalls(node, names) {
|
|
83
|
+
if (!node || typeof node !== "object") return 0;
|
|
84
|
+
if (Array.isArray(node)) return node.reduce((total, child) => total + countServerFnCalls(child, names), 0);
|
|
85
|
+
const record = node;
|
|
86
|
+
let count = 0;
|
|
87
|
+
if (record.type === "CallExpression") {
|
|
88
|
+
const callee = record["callee"];
|
|
89
|
+
if (callee?.type === "Identifier" && callee.name && names.has(callee.name)) count += 1;
|
|
90
|
+
}
|
|
91
|
+
for (const key of Object.keys(record)) count += countServerFnCalls(record[key], names);
|
|
92
|
+
return count;
|
|
93
|
+
}
|
|
94
|
+
//#endregion
|
|
95
|
+
export { scrubServerFnModule };
|
|
96
|
+
|
|
97
|
+
//# sourceMappingURL=server-fn-client-transform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-fn-client-transform.js","names":[],"sources":["../../../src/lib/server-fn-client-transform.ts"],"sourcesContent":["import { parseSync } from 'oxc-parser';\n// Light subpath (no vite/nitro barrel): the built scrub is loaded outside a\n// build too, so it must not pull the plugin graph in.\nimport { deriveServerFnId } from '@analogjs/vite-plugin-nitro/server-fn-id';\n\n/**\n * Client-build scrub + proxy generation for server functions.\n *\n * On the client build a `*.server.ts` module must not carry server handler\n * bodies (which `inject()` server services, read the request, hit the database).\n * But the client still imports the named `serverFn` exports to dispatch them.\n * This transform regenerates the module so each `export const NAME = serverFn(\n * config, handler)` becomes a call to the client-safe `createServerFnRef`\n * factory — keeping the `{ id, url, method }` metadata `injectServerFn` needs,\n * dropping the handler and every server-only import. Because the regenerated\n * module references nothing else, the server code tree-shakes away entirely.\n *\n * Returns the new module source, or `null` when the module defines no server\n * function (the caller falls back to the page-endpoint scrub for those).\n */\nexport interface ServerFnProxy {\n name: string;\n id: string;\n method: 'GET' | 'POST';\n}\n\nexport interface ScrubResult {\n code: string;\n proxies: ServerFnProxy[];\n hadDefaultExport: boolean;\n}\n\nconst SERVER_FN_SOURCE = '@analogjs/router/server';\n\nexport function scrubServerFnModule(\n code: string,\n fileId = 'file.ts',\n): ScrubResult | null {\n // Cheap bail-out before parsing: no `serverFn` token, nothing to do.\n if (!code.includes('serverFn')) {\n return null;\n }\n\n const { program } = parseSync(fileId, code);\n const body = (program as { body: any[] }).body;\n\n const serverFnLocalNames = collectServerFnLocalNames(body);\n const hadDefaultExport = body.some(\n (node) => node.type === 'ExportDefaultDeclaration',\n );\n\n const proxies: ServerFnProxy[] = [];\n for (const node of body) {\n if (\n node.type !== 'ExportNamedDeclaration' ||\n node.declaration?.type !== 'VariableDeclaration'\n ) {\n continue;\n }\n for (const declarator of node.declaration.declarations) {\n if (\n declarator.id?.type !== 'Identifier' ||\n declarator.init?.type !== 'CallExpression' ||\n declarator.init.callee?.type !== 'Identifier' ||\n !serverFnLocalNames.has(declarator.init.callee.name)\n ) {\n continue;\n }\n proxies.push(\n toProxy(declarator.id.name, declarator.init.arguments ?? [], fileId),\n );\n }\n }\n\n // Every `serverFn(...)` call in the module must have become a proxy. If one\n // did not, the module uses a shape this transform cannot rewrite (e.g.\n // `const fn = serverFn(...); export { fn };`) and returning `null` would leave\n // it untouched — shipping the handler and its server imports to the browser.\n // Fail the build instead: a scrub that silently opts out is a code leak.\n const callCount = countServerFnCalls(program, serverFnLocalNames);\n if (callCount > proxies.length) {\n throw new Error(\n `[analog] ${fileId}: found ${callCount} serverFn call(s) but could only rewrite ${proxies.length} for the client build. ` +\n `Server functions must be declared as a directly exported const — \\`export const name = serverFn(…)\\` — so the handler can be stripped from the browser bundle.`,\n );\n }\n\n if (proxies.length === 0) {\n return null;\n }\n\n const lines = [\n `import { createServerFnRef } from '@analogjs/router';`,\n '',\n ...proxies.map(\n (p) =>\n `export const ${p.name} = createServerFnRef({ id: ${JSON.stringify(\n p.id,\n )}, method: ${JSON.stringify(p.method)} });`,\n ),\n ];\n // A file that is also a page endpoint keeps the load-shim the router expects.\n if (hadDefaultExport) {\n lines.push('', 'export default undefined;');\n }\n\n return { code: lines.join('\\n') + '\\n', proxies, hadDefaultExport };\n}\n\n/** Local identifier(s) `serverFn` is imported under from `@analogjs/router/server`. */\nfunction collectServerFnLocalNames(body: any[]): Set<string> {\n const names = new Set<string>();\n for (const node of body) {\n if (\n node.type !== 'ImportDeclaration' ||\n node.source?.value !== SERVER_FN_SOURCE\n ) {\n continue;\n }\n for (const spec of node.specifiers ?? []) {\n if (\n spec.type === 'ImportSpecifier' &&\n spec.imported?.name === 'serverFn'\n ) {\n names.add(spec.local.name);\n }\n }\n }\n // Fallback: if the module never imported it (unusual), still match the\n // conventional name so a mis-resolved import doesn't leak server code.\n if (names.size === 0) {\n names.add('serverFn');\n }\n return names;\n}\n\nfunction toProxy(name: string, args: any[], fileId: string): ServerFnProxy {\n const method = methodForCall(name, args, fileId);\n // The id is derived, never read from source — same algorithm the server\n // registration uses, so client proxy and server route always match.\n return { name, id: deriveServerFnId(fileId, name), method };\n}\n\n/**\n * Resolve the HTTP method from the call shape, matching the runtime overloads:\n * `serverFn(handler)` ⇒ GET, `serverFn(schema, handler)` ⇒ POST,\n * `serverFn(config, handler)` ⇒ config-driven.\n */\nfunction methodForCall(\n name: string,\n args: any[],\n fileId: string,\n): 'GET' | 'POST' {\n const arg0 = args[0];\n\n // serverFn(handler) — input-less GET.\n if (isFunctionNode(arg0)) return 'GET';\n\n // serverFn(config, handler) — read method/input off the object.\n if (arg0?.type === 'ObjectExpression') {\n let method: 'GET' | 'POST' | undefined;\n let hasInput = false;\n for (const prop of arg0.properties ?? []) {\n if (prop.type !== 'Property') continue;\n const key =\n prop.key?.type === 'Identifier' ? prop.key.name : prop.key?.value;\n if (key === 'method' && isStringLiteral(prop.value)) {\n method = prop.value.value as 'GET' | 'POST';\n } else if (key === 'input') {\n hasInput = true;\n }\n }\n if (method === 'GET' && hasInput) {\n throw new Error(\n `[analog] serverFn \"${name}\" in ${fileId} declares method: 'GET' with an input schema; GET carries no body. Use POST or drop the input.`,\n );\n }\n return method ?? (hasInput ? 'POST' : 'GET');\n }\n\n // serverFn(schema, handler) — a schema ⇒ POST + input.\n if (arg0 && args.length >= 2) return 'POST';\n\n throw new Error(\n `[analog] serverFn \"${name}\" in ${fileId} must be called as serverFn(handler), serverFn(schema, handler), or serverFn(config, handler).`,\n );\n}\n\nfunction isFunctionNode(node: any): boolean {\n return (\n node?.type === 'ArrowFunctionExpression' ||\n node?.type === 'FunctionExpression'\n );\n}\n\nfunction isStringLiteral(\n node: any,\n): node is { type: 'Literal'; value: string } {\n return node?.type === 'Literal' && typeof node.value === 'string';\n}\n\n/** Every `serverFn(...)` call anywhere in the module, not just exported ones. */\nfunction countServerFnCalls(node: unknown, names: Set<string>): number {\n if (!node || typeof node !== 'object') {\n return 0;\n }\n if (Array.isArray(node)) {\n return node.reduce(\n (total, child) => total + countServerFnCalls(child, names),\n 0,\n );\n }\n\n const record = node as Record<string, unknown> & { type?: string };\n let count = 0;\n if (record.type === 'CallExpression') {\n const callee = record['callee'] as { type?: string; name?: string };\n if (\n callee?.type === 'Identifier' &&\n callee.name &&\n names.has(callee.name)\n ) {\n count += 1;\n }\n }\n for (const key of Object.keys(record)) {\n count += countServerFnCalls(record[key], names);\n }\n return count;\n}\n"],"mappings":";;;AAgCA,IAAM,mBAAmB;AAEzB,SAAgB,oBACd,MACA,SAAS,WACW;AAEpB,KAAI,CAAC,KAAK,SAAS,WAAW,CAC5B,QAAO;CAGT,MAAM,EAAE,YAAY,UAAU,QAAQ,KAAK;CAC3C,MAAM,OAAQ,QAA4B;CAE1C,MAAM,qBAAqB,0BAA0B,KAAK;CAC1D,MAAM,mBAAmB,KAAK,MAC3B,SAAS,KAAK,SAAS,2BACzB;CAED,MAAM,UAA2B,EAAE;AACnC,MAAK,MAAM,QAAQ,MAAM;AACvB,MACE,KAAK,SAAS,4BACd,KAAK,aAAa,SAAS,sBAE3B;AAEF,OAAK,MAAM,cAAc,KAAK,YAAY,cAAc;AACtD,OACE,WAAW,IAAI,SAAS,gBACxB,WAAW,MAAM,SAAS,oBAC1B,WAAW,KAAK,QAAQ,SAAS,gBACjC,CAAC,mBAAmB,IAAI,WAAW,KAAK,OAAO,KAAK,CAEpD;AAEF,WAAQ,KACN,QAAQ,WAAW,GAAG,MAAM,WAAW,KAAK,aAAa,EAAE,EAAE,OAAO,CACrE;;;CASL,MAAM,YAAY,mBAAmB,SAAS,mBAAmB;AACjE,KAAI,YAAY,QAAQ,OACtB,OAAM,IAAI,MACR,YAAY,OAAO,UAAU,UAAU,2CAA2C,QAAQ,OAAO,uLAElG;AAGH,KAAI,QAAQ,WAAW,EACrB,QAAO;CAGT,MAAM,QAAQ;EACZ;EACA;EACA,GAAG,QAAQ,KACR,MACC,gBAAgB,EAAE,KAAK,6BAA6B,KAAK,UACvD,EAAE,GACH,CAAC,YAAY,KAAK,UAAU,EAAE,OAAO,CAAC,MAC1C;EACF;AAED,KAAI,iBACF,OAAM,KAAK,IAAI,4BAA4B;AAG7C,QAAO;EAAE,MAAM,MAAM,KAAK,KAAK,GAAG;EAAM;EAAS;EAAkB;;;AAIrE,SAAS,0BAA0B,MAA0B;CAC3D,MAAM,wBAAQ,IAAI,KAAa;AAC/B,MAAK,MAAM,QAAQ,MAAM;AACvB,MACE,KAAK,SAAS,uBACd,KAAK,QAAQ,UAAU,iBAEvB;AAEF,OAAK,MAAM,QAAQ,KAAK,cAAc,EAAE,CACtC,KACE,KAAK,SAAS,qBACd,KAAK,UAAU,SAAS,WAExB,OAAM,IAAI,KAAK,MAAM,KAAK;;AAMhC,KAAI,MAAM,SAAS,EACjB,OAAM,IAAI,WAAW;AAEvB,QAAO;;AAGT,SAAS,QAAQ,MAAc,MAAa,QAA+B;CACzE,MAAM,SAAS,cAAc,MAAM,MAAM,OAAO;AAGhD,QAAO;EAAE;EAAM,IAAI,iBAAiB,QAAQ,KAAK;EAAE;EAAQ;;;;;;;AAQ7D,SAAS,cACP,MACA,MACA,QACgB;CAChB,MAAM,OAAO,KAAK;AAGlB,KAAI,eAAe,KAAK,CAAE,QAAO;AAGjC,KAAI,MAAM,SAAS,oBAAoB;EACrC,IAAI;EACJ,IAAI,WAAW;AACf,OAAK,MAAM,QAAQ,KAAK,cAAc,EAAE,EAAE;AACxC,OAAI,KAAK,SAAS,WAAY;GAC9B,MAAM,MACJ,KAAK,KAAK,SAAS,eAAe,KAAK,IAAI,OAAO,KAAK,KAAK;AAC9D,OAAI,QAAQ,YAAY,gBAAgB,KAAK,MAAM,CACjD,UAAS,KAAK,MAAM;YACX,QAAQ,QACjB,YAAW;;AAGf,MAAI,WAAW,SAAS,SACtB,OAAM,IAAI,MACR,sBAAsB,KAAK,OAAO,OAAO,gGAC1C;AAEH,SAAO,WAAW,WAAW,SAAS;;AAIxC,KAAI,QAAQ,KAAK,UAAU,EAAG,QAAO;AAErC,OAAM,IAAI,MACR,sBAAsB,KAAK,OAAO,OAAO,gGAC1C;;AAGH,SAAS,eAAe,MAAoB;AAC1C,QACE,MAAM,SAAS,6BACf,MAAM,SAAS;;AAInB,SAAS,gBACP,MAC4C;AAC5C,QAAO,MAAM,SAAS,aAAa,OAAO,KAAK,UAAU;;;AAI3D,SAAS,mBAAmB,MAAe,OAA4B;AACrE,KAAI,CAAC,QAAQ,OAAO,SAAS,SAC3B,QAAO;AAET,KAAI,MAAM,QAAQ,KAAK,CACrB,QAAO,KAAK,QACT,OAAO,UAAU,QAAQ,mBAAmB,OAAO,MAAM,EAC1D,EACD;CAGH,MAAM,SAAS;CACf,IAAI,QAAQ;AACZ,KAAI,OAAO,SAAS,kBAAkB;EACpC,MAAM,SAAS,OAAO;AACtB,MACE,QAAQ,SAAS,gBACjB,OAAO,QACP,MAAM,IAAI,OAAO,KAAK,CAEtB,UAAS;;AAGb,MAAK,MAAM,OAAO,OAAO,KAAK,OAAO,CACnC,UAAS,mBAAmB,OAAO,MAAM,MAAM;AAEjD,QAAO"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
/**
|
|
3
|
+
* Minimum Angular major whose compiled `@angular/core` FESM matches the
|
|
4
|
+
* streaming patch anchors. Incremental hydration is a stable public API from
|
|
5
|
+
* v20 (`withIncrementalHydration`, `@publicApi 20.0`), but v20's FESM inlines
|
|
6
|
+
* the injection anchor's `DeferBlockStateEnd` profiler event to its numeric
|
|
7
|
+
* ordinal, whereas v21+ keeps the symbolic `ProfilerEvent.DeferBlockStateEnd`
|
|
8
|
+
* form the anchor matches on. Keying on the literal ordinal would be fragile
|
|
9
|
+
* (enum values shift between versions), so v21 is the floor.
|
|
10
|
+
*/
|
|
11
|
+
export declare const MIN_STREAMING_ANGULAR_MAJOR = 21;
|
|
12
|
+
/**
|
|
13
|
+
* Whether the streaming SSR patch can be applied to the installed Angular.
|
|
14
|
+
* A `null` major (version undetectable) returns `true`: don't block on a failed
|
|
15
|
+
* detection — the plugin's anchor-drift detection catches a real mismatch at
|
|
16
|
+
* build time and falls back to buffered rendering.
|
|
17
|
+
*/
|
|
18
|
+
export declare function streamingSupportedOnAngular(major: number | null): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Pure transform that injects the streaming-SSR per-block resolution hook into
|
|
21
|
+
* `@angular/core`'s compiled output. It is the same class of transform as
|
|
22
|
+
* `i18nDefRegistryPlugin` in ./ssr-build-plugin — a string patch of Angular's
|
|
23
|
+
* bundle applied only to SSR builds. Exported separately so it can be unit
|
|
24
|
+
* tested against a bundle string.
|
|
25
|
+
*
|
|
26
|
+
* Two edits, both keyed on single-occurrence anchors in the module that holds
|
|
27
|
+
* the defer runtime:
|
|
28
|
+
* 1. Inside `applyDeferBlockState`, fire `globalThis.__analogSsrDeferCapture`
|
|
29
|
+
* when a `@defer` block reaches `Complete` on the server, passing the
|
|
30
|
+
* block's live `lContainer` — this is the per-block resolution signal a
|
|
31
|
+
* streaming renderer consumes.
|
|
32
|
+
* 2. Expose `collectNativeNodesInLContainer` via `globalThis.__analogSsrInternals`
|
|
33
|
+
* so the renderer can serialize a block's subtree.
|
|
34
|
+
*
|
|
35
|
+
* Returns `null` when the module is not the one carrying the defer runtime, so
|
|
36
|
+
* it is a no-op on every other Angular module.
|
|
37
|
+
*/
|
|
38
|
+
export declare function injectDeferStreamingHook(code: string): string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Classify an `@angular/core` module for the streaming patch. The patch anchors
|
|
41
|
+
* on internal symbol names, so when Angular changes those the transform would
|
|
42
|
+
* otherwise become a silent no-op and streaming would degrade to buffered with
|
|
43
|
+
* no signal. This distinguishes "not the target module" (skip quietly) from
|
|
44
|
+
* "this IS the `@defer` runtime module but the anchors drifted" (worth warning).
|
|
45
|
+
*/
|
|
46
|
+
export declare function inspectAngularCoreModule(code: string): {
|
|
47
|
+
kind: "not-target";
|
|
48
|
+
} | {
|
|
49
|
+
kind: "patchable";
|
|
50
|
+
} | {
|
|
51
|
+
kind: "drifted";
|
|
52
|
+
reason: string;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Vite plugin that applies {@link injectDeferStreamingHook} to `@angular/core`
|
|
56
|
+
* during SSR builds, so `@analogjs/router`'s `renderStream` can flush `@defer`
|
|
57
|
+
* blocks as they resolve on the server — without hand-patching node_modules.
|
|
58
|
+
*
|
|
59
|
+
* Only active for SSR. Mirrors the enforce/filter/ssr-gate shape of
|
|
60
|
+
* `i18nDefRegistryPlugin`. If the `@defer` runtime module is found but its
|
|
61
|
+
* anchors have drifted (Angular changed internals), or if it is never
|
|
62
|
+
* encountered at all, the plugin warns rather than silently producing a build
|
|
63
|
+
* that falls back to buffered rendering.
|
|
64
|
+
*/
|
|
65
|
+
export declare function deferStreamingPlugin(): Plugin;
|