@llui/compiler-ssr 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,wBAAwB,GAAG,IAAI,CAoGjC;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CA4B7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,wBAAwB,GAAG,IAAI,CAwGjC;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CA4B7D"}
package/dist/index.js CHANGED
@@ -108,8 +108,12 @@ export function transformUseClientSsr(source, _filename) {
108
108
  // Imports, `import type`, enum declarations, plain (non-export)
109
109
  // variable statements — dropped from the stub output.
110
110
  }
111
- // Build the generated module source.
112
- const lines = ["import { __clientOnlyStub } from '@llui/dom'", ''];
111
+ // Build the generated module source. `__clientOnlyStub` lives on
112
+ // `@llui/dom/internal` (not the root barrel) so the vite-plugin's
113
+ // post-bundle rename pass can't rewrite the identifier across a
114
+ // module-external import boundary. See @llui/compiler/emit-names.ts
115
+ // § COMPILER_DOM_INTERNAL_IMPORTS for the contract.
116
+ const lines = ["import { __clientOnlyStub } from '@llui/dom/internal'", ''];
113
117
  for (const name of namedExports) {
114
118
  lines.push(`export const ${name} = __clientOnlyStub(${JSON.stringify(name)})`);
115
119
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// @llui/compiler-ssr — SSR support (opt-in).\n//\n// Handles the `'use client'` directive: scans for it cheaply with\n// `hasUseClientDirective`, then rewrites client-only modules into\n// stubs that the SSR build can ship via `transformUseClientSsr`.\n//\n// `@llui/vike` calls these directly from its Vite plugin's transform\n// hook when `ssr: true`. The plugin gates on `hasUseClientDirective`\n// to avoid the parse cost when the directive isn't present.\n//\n// Owned by this package since v2c/decomp-25 (moved verbatim from\n// @llui/compiler's transform.ts).\n\nimport ts from 'typescript'\n\n// ── 'use client' directive ───────────────────────────────────────\n\nexport interface UseClientTransformResult {\n output: string\n warnings: string[]\n}\n\n/**\n * If `source` begins with a `'use client'` directive, generate a stub\n * replacement for the SSR build. Every `export const X = <expr>` becomes\n * `export const X = __clientOnlyStub('X')`, every `export function X`\n * becomes a stub, and `export default <expr>` becomes a default stub.\n * Returns `null` if the directive is absent (caller should fall through\n * to the normal compiler pass).\n *\n * The client build is expected to skip this path entirely — Vite passes\n * `{ ssr: false }` there, and the plugin checks that before invoking\n * this function.\n *\n * Shapes this v1 does NOT handle (emits a warning + leaves them out of\n * the stub output):\n *\n * - `export function foo() {}` and `export class Foo {}` — rewritten\n * as stubs but the caller may be surprised that `foo` and `Foo` are\n * ComponentDef-shaped objects during SSR.\n * - `export { a, b } from './other.js'` — re-export forms are not\n * detected; they pass through and will still pull `./other` into\n * the SSR graph.\n * - `export * from './other.js'` — same as above.\n * - `export type ...` — type exports are erased by TS so nothing to\n * stub; left untouched.\n */\nexport function transformUseClientSsr(\n source: string,\n _filename: string,\n): UseClientTransformResult | null {\n const sourceFile = ts.createSourceFile('input.ts', source, ts.ScriptTarget.Latest, true)\n\n // Find the first non-comment, non-directive-whitespace statement.\n // 'use client' should be the literal first statement in the file.\n const first = sourceFile.statements[0]\n if (!first) return null\n if (!ts.isExpressionStatement(first)) return null\n if (!ts.isStringLiteral(first.expression)) return null\n if (first.expression.text !== 'use client') return null\n\n const warnings: string[] = []\n const namedExports: string[] = []\n let hasDefaultExport = false\n\n for (const stmt of sourceFile.statements) {\n // The `'use client'` directive itself — skip.\n if (stmt === first) continue\n\n // `export const NAME = ...` and `export let NAME = ...`\n if (\n ts.isVariableStatement(stmt) &&\n stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)\n ) {\n for (const decl of stmt.declarationList.declarations) {\n if (ts.isIdentifier(decl.name)) {\n namedExports.push(decl.name.text)\n } else {\n warnings.push(\n '[llui/use-client] destructured `export const { ... }` is not supported; each binding would have to be stubbed individually. Refactor to one `export const` per value.',\n )\n }\n }\n continue\n }\n\n // `export function NAME() {}`\n if (\n ts.isFunctionDeclaration(stmt) &&\n stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) &&\n stmt.name\n ) {\n namedExports.push(stmt.name.text)\n continue\n }\n\n // `export class NAME {}`\n if (\n ts.isClassDeclaration(stmt) &&\n stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) &&\n stmt.name\n ) {\n namedExports.push(stmt.name.text)\n continue\n }\n\n // `export default ...`\n if (\n ts.isExportAssignment(stmt) ||\n (ts.isFunctionDeclaration(stmt) &&\n stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword))\n ) {\n hasDefaultExport = true\n continue\n }\n\n // `export { a, b }` / `export { a } from './x.js'` / `export * from './x.js'`\n if (ts.isExportDeclaration(stmt)) {\n if (stmt.moduleSpecifier) {\n warnings.push(\n \"[llui/use-client] `export ... from '...'` re-export forms still pull the source module into the SSR graph and bypass stubbing. Either drop the re-export or move the 'use client' directive to the source module.\",\n )\n } else if (stmt.exportClause && ts.isNamedExports(stmt.exportClause)) {\n for (const spec of stmt.exportClause.elements) {\n namedExports.push((spec.name ?? spec.propertyName!).text)\n }\n }\n continue\n }\n\n // Type-only statements are erased at runtime — nothing to stub.\n if (ts.isTypeAliasDeclaration(stmt) || ts.isInterfaceDeclaration(stmt)) continue\n\n // Imports, `import type`, enum declarations, plain (non-export)\n // variable statements — dropped from the stub output.\n }\n\n // Build the generated module source.\n const lines: string[] = [\"import { __clientOnlyStub } from '@llui/dom'\", '']\n for (const name of namedExports) {\n lines.push(`export const ${name} = __clientOnlyStub(${JSON.stringify(name)})`)\n }\n if (hasDefaultExport) {\n lines.push('export default __clientOnlyStub(\"default\")')\n }\n\n return {\n output: lines.join('\\n') + '\\n',\n warnings,\n }\n}\n\n/**\n * Check whether `source`'s first statement is a `'use client'` directive.\n * Cheap string scan so the caller can decide which transform to run\n * without parsing the whole file twice.\n */\nexport function hasUseClientDirective(source: string): boolean {\n // Skip leading whitespace and block/line comments; look for the\n // first token. A full parse is overkill here — users who write\n // `'use client'` in any other position (inside a function, after\n // imports) aren't using the directive as React/Vercel define it.\n let i = 0\n const len = source.length\n while (i < len) {\n const ch = source[i]!\n if (ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '\\r') {\n i++\n continue\n }\n if (source.startsWith('//', i)) {\n const nl = source.indexOf('\\n', i)\n if (nl === -1) return false\n i = nl + 1\n continue\n }\n if (source.startsWith('/*', i)) {\n const end = source.indexOf('*/', i + 2)\n if (end === -1) return false\n i = end + 2\n continue\n }\n break\n }\n return source.startsWith(\"'use client'\", i) || source.startsWith('\"use client\"', i)\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// @llui/compiler-ssr — SSR support (opt-in).\n//\n// Handles the `'use client'` directive: scans for it cheaply with\n// `hasUseClientDirective`, then rewrites client-only modules into\n// stubs that the SSR build can ship via `transformUseClientSsr`.\n//\n// `@llui/vike` calls these directly from its Vite plugin's transform\n// hook when `ssr: true`. The plugin gates on `hasUseClientDirective`\n// to avoid the parse cost when the directive isn't present.\n//\n// Owned by this package since v2c/decomp-25 (moved verbatim from\n// @llui/compiler's transform.ts).\n\nimport ts from 'typescript'\n\n// ── 'use client' directive ───────────────────────────────────────\n\nexport interface UseClientTransformResult {\n output: string\n warnings: string[]\n}\n\n/**\n * If `source` begins with a `'use client'` directive, generate a stub\n * replacement for the SSR build. Every `export const X = <expr>` becomes\n * `export const X = __clientOnlyStub('X')`, every `export function X`\n * becomes a stub, and `export default <expr>` becomes a default stub.\n * Returns `null` if the directive is absent (caller should fall through\n * to the normal compiler pass).\n *\n * The client build is expected to skip this path entirely — Vite passes\n * `{ ssr: false }` there, and the plugin checks that before invoking\n * this function.\n *\n * Shapes this v1 does NOT handle (emits a warning + leaves them out of\n * the stub output):\n *\n * - `export function foo() {}` and `export class Foo {}` — rewritten\n * as stubs but the caller may be surprised that `foo` and `Foo` are\n * ComponentDef-shaped objects during SSR.\n * - `export { a, b } from './other.js'` — re-export forms are not\n * detected; they pass through and will still pull `./other` into\n * the SSR graph.\n * - `export * from './other.js'` — same as above.\n * - `export type ...` — type exports are erased by TS so nothing to\n * stub; left untouched.\n */\nexport function transformUseClientSsr(\n source: string,\n _filename: string,\n): UseClientTransformResult | null {\n const sourceFile = ts.createSourceFile('input.ts', source, ts.ScriptTarget.Latest, true)\n\n // Find the first non-comment, non-directive-whitespace statement.\n // 'use client' should be the literal first statement in the file.\n const first = sourceFile.statements[0]\n if (!first) return null\n if (!ts.isExpressionStatement(first)) return null\n if (!ts.isStringLiteral(first.expression)) return null\n if (first.expression.text !== 'use client') return null\n\n const warnings: string[] = []\n const namedExports: string[] = []\n let hasDefaultExport = false\n\n for (const stmt of sourceFile.statements) {\n // The `'use client'` directive itself — skip.\n if (stmt === first) continue\n\n // `export const NAME = ...` and `export let NAME = ...`\n if (\n ts.isVariableStatement(stmt) &&\n stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)\n ) {\n for (const decl of stmt.declarationList.declarations) {\n if (ts.isIdentifier(decl.name)) {\n namedExports.push(decl.name.text)\n } else {\n warnings.push(\n '[llui/use-client] destructured `export const { ... }` is not supported; each binding would have to be stubbed individually. Refactor to one `export const` per value.',\n )\n }\n }\n continue\n }\n\n // `export function NAME() {}`\n if (\n ts.isFunctionDeclaration(stmt) &&\n stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) &&\n stmt.name\n ) {\n namedExports.push(stmt.name.text)\n continue\n }\n\n // `export class NAME {}`\n if (\n ts.isClassDeclaration(stmt) &&\n stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) &&\n stmt.name\n ) {\n namedExports.push(stmt.name.text)\n continue\n }\n\n // `export default ...`\n if (\n ts.isExportAssignment(stmt) ||\n (ts.isFunctionDeclaration(stmt) &&\n stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword))\n ) {\n hasDefaultExport = true\n continue\n }\n\n // `export { a, b }` / `export { a } from './x.js'` / `export * from './x.js'`\n if (ts.isExportDeclaration(stmt)) {\n if (stmt.moduleSpecifier) {\n warnings.push(\n \"[llui/use-client] `export ... from '...'` re-export forms still pull the source module into the SSR graph and bypass stubbing. Either drop the re-export or move the 'use client' directive to the source module.\",\n )\n } else if (stmt.exportClause && ts.isNamedExports(stmt.exportClause)) {\n for (const spec of stmt.exportClause.elements) {\n namedExports.push((spec.name ?? spec.propertyName!).text)\n }\n }\n continue\n }\n\n // Type-only statements are erased at runtime — nothing to stub.\n if (ts.isTypeAliasDeclaration(stmt) || ts.isInterfaceDeclaration(stmt)) continue\n\n // Imports, `import type`, enum declarations, plain (non-export)\n // variable statements — dropped from the stub output.\n }\n\n // Build the generated module source. `__clientOnlyStub` lives on\n // `@llui/dom/internal` (not the root barrel) so the vite-plugin's\n // post-bundle rename pass can't rewrite the identifier across a\n // module-external import boundary. See @llui/compiler/emit-names.ts\n // § COMPILER_DOM_INTERNAL_IMPORTS for the contract.\n const lines: string[] = [\"import { __clientOnlyStub } from '@llui/dom/internal'\", '']\n for (const name of namedExports) {\n lines.push(`export const ${name} = __clientOnlyStub(${JSON.stringify(name)})`)\n }\n if (hasDefaultExport) {\n lines.push('export default __clientOnlyStub(\"default\")')\n }\n\n return {\n output: lines.join('\\n') + '\\n',\n warnings,\n }\n}\n\n/**\n * Check whether `source`'s first statement is a `'use client'` directive.\n * Cheap string scan so the caller can decide which transform to run\n * without parsing the whole file twice.\n */\nexport function hasUseClientDirective(source: string): boolean {\n // Skip leading whitespace and block/line comments; look for the\n // first token. A full parse is overkill here — users who write\n // `'use client'` in any other position (inside a function, after\n // imports) aren't using the directive as React/Vercel define it.\n let i = 0\n const len = source.length\n while (i < len) {\n const ch = source[i]!\n if (ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '\\r') {\n i++\n continue\n }\n if (source.startsWith('//', i)) {\n const nl = source.indexOf('\\n', i)\n if (nl === -1) return false\n i = nl + 1\n continue\n }\n if (source.startsWith('/*', i)) {\n const end = source.indexOf('*/', i + 2)\n if (end === -1) return false\n i = end + 2\n continue\n }\n break\n }\n return source.startsWith(\"'use client'\", i) || source.startsWith('\"use client\"', i)\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llui/compiler-ssr",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "exports": {
@@ -16,7 +16,7 @@
16
16
  ],
17
17
  "dependencies": {
18
18
  "typescript": "^6.0.2",
19
- "@llui/compiler": "0.3.1"
19
+ "@llui/compiler": "0.4.0"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "^22.0.0"