@mindees/compiler 0.9.0 → 0.11.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.
@@ -0,0 +1,18 @@
1
+ import { CompileResult, Diagnostic } from "./types.js";
2
+
3
+ //#region src/budget.d.ts
4
+ /** A per-module performance budget. A field left undefined isn't enforced. */
5
+ interface BudgetOptions {
6
+ /** Max compiled output size in **bytes** (UTF-8). */
7
+ readonly maxBytes?: number;
8
+ /** Max total elements in the module's UI tree (pre-flatten count). */
9
+ readonly maxElements?: number;
10
+ }
11
+ /**
12
+ * Check a compile result against `budget`. Returns **error** diagnostics for every exceeded limit
13
+ * (empty when within budget). `compileChecked` refuses to emit when any are present.
14
+ */
15
+ declare function checkBudget(result: Pick<CompileResult, 'code' | 'stats'>, budget: BudgetOptions): Diagnostic[];
16
+ //#endregion
17
+ export { BudgetOptions, checkBudget };
18
+ //# sourceMappingURL=budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget.d.ts","names":[],"sources":["../src/budget.ts"],"mappings":";;;;UAYiB,aAAA;EAiBF;EAAA,SAfJ,QAAA;EAgBD;EAAA,SAdC,WAAW;AAAA;;;;;iBAYN,WAAA,CACd,MAAA,EAAQ,IAAA,CAAK,aAAA,qBACb,MAAA,EAAQ,aAAA,GACP,UAAA"}
package/dist/budget.js ADDED
@@ -0,0 +1,30 @@
1
+ //#region src/budget.ts
2
+ /** UTF-8 byte length of `text` (platform-neutral; no `Buffer`). */
3
+ function byteLength(text) {
4
+ return new TextEncoder().encode(text).length;
5
+ }
6
+ /**
7
+ * Check a compile result against `budget`. Returns **error** diagnostics for every exceeded limit
8
+ * (empty when within budget). `compileChecked` refuses to emit when any are present.
9
+ */
10
+ function checkBudget(result, budget) {
11
+ const diagnostics = [];
12
+ if (budget.maxBytes !== void 0) {
13
+ const bytes = byteLength(result.code);
14
+ if (bytes > budget.maxBytes) diagnostics.push({
15
+ severity: "error",
16
+ code: "MDC_BUDGET_BYTES",
17
+ message: `Bundle size ${bytes} B exceeds the budget of ${budget.maxBytes} B (over by ${bytes - budget.maxBytes} B). Split the screen, lazy-load, or raise the budget.`
18
+ });
19
+ }
20
+ if (budget.maxElements !== void 0 && result.stats.totalElements > budget.maxElements) diagnostics.push({
21
+ severity: "error",
22
+ code: "MDC_BUDGET_ELEMENTS",
23
+ message: `${result.stats.totalElements} UI elements exceed the budget of ${budget.maxElements}. Virtualize long lists (List/For) or split the screen.`
24
+ });
25
+ return diagnostics;
26
+ }
27
+ //#endregion
28
+ export { checkBudget };
29
+
30
+ //# sourceMappingURL=budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget.js","names":[],"sources":["../src/budget.ts"],"sourcesContent":["/**\n * Build-time **performance budgets** (spec §12: \"the compiler fails the build if a screen exceeds\n * configured … bundle-size budgets — so '100% optimized' is enforced, not aspirational\"). Unlike the\n * perf-lint (warnings), a budget violation is an **error** that refuses to emit — neither React Native\n * nor Flutter enforces a perf budget at build time.\n *\n * @module\n */\n\nimport type { CompileResult, Diagnostic } from './types'\n\n/** A per-module performance budget. A field left undefined isn't enforced. */\nexport interface BudgetOptions {\n /** Max compiled output size in **bytes** (UTF-8). */\n readonly maxBytes?: number\n /** Max total elements in the module's UI tree (pre-flatten count). */\n readonly maxElements?: number\n}\n\n/** UTF-8 byte length of `text` (platform-neutral; no `Buffer`). */\nfunction byteLength(text: string): number {\n return new TextEncoder().encode(text).length\n}\n\n/**\n * Check a compile result against `budget`. Returns **error** diagnostics for every exceeded limit\n * (empty when within budget). `compileChecked` refuses to emit when any are present.\n */\nexport function checkBudget(\n result: Pick<CompileResult, 'code' | 'stats'>,\n budget: BudgetOptions,\n): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n if (budget.maxBytes !== undefined) {\n const bytes = byteLength(result.code)\n if (bytes > budget.maxBytes) {\n diagnostics.push({\n severity: 'error',\n code: 'MDC_BUDGET_BYTES',\n message: `Bundle size ${bytes} B exceeds the budget of ${budget.maxBytes} B (over by ${bytes - budget.maxBytes} B). Split the screen, lazy-load, or raise the budget.`,\n })\n }\n }\n if (budget.maxElements !== undefined && result.stats.totalElements > budget.maxElements) {\n diagnostics.push({\n severity: 'error',\n code: 'MDC_BUDGET_ELEMENTS',\n message: `${result.stats.totalElements} UI elements exceed the budget of ${budget.maxElements}. Virtualize long lists (List/For) or split the screen.`,\n })\n }\n return diagnostics\n}\n"],"mappings":";;AAoBA,SAAS,WAAW,MAAsB;CACxC,OAAO,IAAI,YAAY,EAAE,OAAO,IAAI,EAAE;AACxC;;;;;AAMA,SAAgB,YACd,QACA,QACc;CACd,MAAM,cAA4B,CAAC;CACnC,IAAI,OAAO,aAAa,KAAA,GAAW;EACjC,MAAM,QAAQ,WAAW,OAAO,IAAI;EACpC,IAAI,QAAQ,OAAO,UACjB,YAAY,KAAK;GACf,UAAU;GACV,MAAM;GACN,SAAS,eAAe,MAAM,2BAA2B,OAAO,SAAS,cAAc,QAAQ,OAAO,SAAS;EACjH,CAAC;CAEL;CACA,IAAI,OAAO,gBAAgB,KAAA,KAAa,OAAO,MAAM,gBAAgB,OAAO,aAC1E,YAAY,KAAK;EACf,UAAU;EACV,MAAM;EACN,SAAS,GAAG,OAAO,MAAM,cAAc,oCAAoC,OAAO,YAAY;CAChG,CAAC;CAEH,OAAO;AACT"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { NativeTarget, compileToNative } from "./aot.js";
2
2
  import { PerfLintOptions, perfLint } from "./perf-lint.js";
3
3
  import { CompileOptions, CompileResult, CompileStats, Diagnostic, DiagnosticSeverity, MdcPlugin, SourcePosition } from "./types.js";
4
+ import { BudgetOptions, checkBudget } from "./budget.js";
4
5
  import { STATIC_MARKER, createFlattenTransformer } from "./flatten.js";
5
6
  import { GenerateRouteModuleOptions, RouteEntry, RouteManifest, buildRouteManifest, chunkName, fileToRoute, generateRouteModule } from "./routes.js";
6
7
  import { compile, compileChecked } from "./transform.js";
@@ -11,7 +12,7 @@ import { Maturity, NotImplementedError, PackageInfo, notImplemented } from "@min
11
12
  /** The npm package name. */
12
13
  declare const name = "@mindees/compiler";
13
14
  /** The package version. All `@mindees/*` packages share one locked version line. */
14
- declare const VERSION = "0.9.0";
15
+ declare const VERSION = "0.11.0";
15
16
  /**
16
17
  * Current maturity. The build-time optimizer — type-check gate, TSX→createElement
17
18
  * transform, tree-flattening, per-route manifest, plugin API — is implemented
@@ -26,5 +27,5 @@ declare const maturity: Maturity;
26
27
  */
27
28
  declare const info: PackageInfo;
28
29
  //#endregion
29
- export { type CompileOptions, type CompileResult, type CompileStats, type Diagnostic, type DiagnosticSeverity, type GenerateRouteModuleOptions, type Maturity, type MdcPlugin, type NativeTarget, NotImplementedError, type PackageInfo, type PerfLintOptions, type RouteEntry, type RouteManifest, STATIC_MARKER, type SourcePosition, VERSION, buildRouteManifest, chunkName, compile, compileChecked, compileToNative, createFlattenTransformer, fileToRoute, generateRouteModule, hasErrors, info, maturity, name, notImplemented, perfLint, typecheck };
30
+ export { type BudgetOptions, type CompileOptions, type CompileResult, type CompileStats, type Diagnostic, type DiagnosticSeverity, type GenerateRouteModuleOptions, type Maturity, type MdcPlugin, type NativeTarget, NotImplementedError, type PackageInfo, type PerfLintOptions, type RouteEntry, type RouteManifest, STATIC_MARKER, type SourcePosition, VERSION, buildRouteManifest, checkBudget, chunkName, compile, compileChecked, compileToNative, createFlattenTransformer, fileToRoute, generateRouteModule, hasErrors, info, maturity, name, notImplemented, perfLint, typecheck };
30
31
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;cAmCa,IAAA;;cAGA,OAAA;AAQb;;;;AAAgD;AAOhD;AAPA,cAAa,QAAA,EAAU,QAAyB;;;AAOoC;;;cAAvE,IAAA,EAAM,WAAiE"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;;cAqCa,IAAA;AAGO;AAAA,cAAP,OAAA;;;;AAQmC;AAOhD;;cAPa,QAAA,EAAU,QAAyB;;AAOoC;;;;cAAvE,IAAA,EAAM,WAAiE"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { compileToNative } from "./aot.js";
2
+ import { checkBudget } from "./budget.js";
2
3
  import { STATIC_MARKER, createFlattenTransformer } from "./flatten.js";
3
4
  import { hasErrors, typecheck } from "./typecheck.js";
4
5
  import { perfLint } from "./perf-lint.js";
@@ -9,7 +10,7 @@ import { NotImplementedError, notImplemented } from "@mindees/core";
9
10
  /** The npm package name. */
10
11
  const name = "@mindees/compiler";
11
12
  /** The package version. All `@mindees/*` packages share one locked version line. */
12
- const VERSION = "0.9.0";
13
+ const VERSION = "0.11.0";
13
14
  /**
14
15
  * Current maturity. The build-time optimizer — type-check gate, TSX→createElement
15
16
  * transform, tree-flattening, per-route manifest, plugin API — is implemented
@@ -28,6 +29,6 @@ const info = Object.freeze({
28
29
  maturity
29
30
  });
30
31
  //#endregion
31
- export { NotImplementedError, STATIC_MARKER, VERSION, buildRouteManifest, chunkName, compile, compileChecked, compileToNative, createFlattenTransformer, fileToRoute, generateRouteModule, hasErrors, info, maturity, name, notImplemented, perfLint, typecheck };
32
+ export { NotImplementedError, STATIC_MARKER, VERSION, buildRouteManifest, checkBudget, chunkName, compile, compileChecked, compileToNative, createFlattenTransformer, fileToRoute, generateRouteModule, hasErrors, info, maturity, name, notImplemented, perfLint, typecheck };
32
33
 
33
34
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Maturity, PackageInfo } from '@mindees/core'\nimport { NotImplementedError, notImplemented } from '@mindees/core'\n\n/** TS → native AOT (research track). */\nexport { compileToNative, type NativeTarget } from './aot'\n/** Tree-flattening optimizer pass. */\nexport { createFlattenTransformer, STATIC_MARKER } from './flatten'\n/** Build-time perf-lint (opt-in via `compileChecked(src, { perf: true })`). */\nexport { type PerfLintOptions, perfLint } from './perf-lint'\n/** Per-route code-splitting manifest + file-based route codegen. */\nexport {\n buildRouteManifest,\n chunkName,\n fileToRoute,\n type GenerateRouteModuleOptions,\n generateRouteModule,\n type RouteEntry,\n type RouteManifest,\n} from './routes'\n/** Compile pipeline (TSX → optimized JS). */\nexport { compile, compileChecked } from './transform'\n/** The type-check gate. */\nexport { hasErrors, typecheck } from './typecheck'\n/** Shared types. */\nexport type {\n CompileOptions,\n CompileResult,\n CompileStats,\n Diagnostic,\n DiagnosticSeverity,\n MdcPlugin,\n SourcePosition,\n} from './types'\n\n/** The npm package name. */\nexport const name = '@mindees/compiler'\n\n/** The package version. All `@mindees/*` packages share one locked version line. */\nexport const VERSION = '0.9.0'\n\n/**\n * Current maturity. The build-time optimizer — type-check gate, TSX→createElement\n * transform, tree-flattening, per-route manifest, plugin API — is implemented\n * and tested on the TypeScript Compiler API. TS→native AOT is a research track\n * (throws `NotImplementedError`); the working path is TS → optimized JS.\n */\nexport const maturity: Maturity = 'experimental'\n\n/**\n * Static identity + maturity metadata for this package. Frozen so the\n * self-reported identity tooling introspects cannot be mutated at runtime,\n * matching the `readonly` fields of {@link PackageInfo}.\n */\nexport const info: PackageInfo = Object.freeze({ name, version: VERSION, maturity })\n\nexport type { Maturity, PackageInfo }\nexport { NotImplementedError, notImplemented }\n"],"mappings":";;;;;;;;;AAmCA,MAAa,OAAO;;AAGpB,MAAa,UAAU;;;;;;;AAQvB,MAAa,WAAqB;;;;;;AAOlC,MAAa,OAAoB,OAAO,OAAO;CAAE;CAAM,SAAS;CAAS;AAAS,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Maturity, PackageInfo } from '@mindees/core'\nimport { NotImplementedError, notImplemented } from '@mindees/core'\n\n/** TS → native AOT (research track). */\nexport { compileToNative, type NativeTarget } from './aot'\n/** Build-time performance budget (opt-in via `compileChecked(src, { budget })`) — fails the build. */\nexport { type BudgetOptions, checkBudget } from './budget'\n/** Tree-flattening optimizer pass. */\nexport { createFlattenTransformer, STATIC_MARKER } from './flatten'\n/** Build-time perf-lint (opt-in via `compileChecked(src, { perf: true })`). */\nexport { type PerfLintOptions, perfLint } from './perf-lint'\n/** Per-route code-splitting manifest + file-based route codegen. */\nexport {\n buildRouteManifest,\n chunkName,\n fileToRoute,\n type GenerateRouteModuleOptions,\n generateRouteModule,\n type RouteEntry,\n type RouteManifest,\n} from './routes'\n/** Compile pipeline (TSX → optimized JS). */\nexport { compile, compileChecked } from './transform'\n/** The type-check gate. */\nexport { hasErrors, typecheck } from './typecheck'\n/** Shared types. */\nexport type {\n CompileOptions,\n CompileResult,\n CompileStats,\n Diagnostic,\n DiagnosticSeverity,\n MdcPlugin,\n SourcePosition,\n} from './types'\n\n/** The npm package name. */\nexport const name = '@mindees/compiler'\n\n/** The package version. All `@mindees/*` packages share one locked version line. */\nexport const VERSION = '0.11.0'\n\n/**\n * Current maturity. The build-time optimizer — type-check gate, TSX→createElement\n * transform, tree-flattening, per-route manifest, plugin API — is implemented\n * and tested on the TypeScript Compiler API. TS→native AOT is a research track\n * (throws `NotImplementedError`); the working path is TS → optimized JS.\n */\nexport const maturity: Maturity = 'experimental'\n\n/**\n * Static identity + maturity metadata for this package. Frozen so the\n * self-reported identity tooling introspects cannot be mutated at runtime,\n * matching the `readonly` fields of {@link PackageInfo}.\n */\nexport const info: PackageInfo = Object.freeze({ name, version: VERSION, maturity })\n\nexport type { Maturity, PackageInfo }\nexport { NotImplementedError, notImplemented }\n"],"mappings":";;;;;;;;;;AAqCA,MAAa,OAAO;;AAGpB,MAAa,UAAU;;;;;;;AAQvB,MAAa,WAAqB;;;;;;AAOlC,MAAa,OAAoB,OAAO,OAAO;CAAE;CAAM,SAAS;CAAS;AAAS,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"transform.d.ts","names":[],"sources":["../src/transform.ts"],"mappings":";;;AA+FoF;AAuDpF;;;;;;;AAvDoF,iBAApE,OAAA,CAAQ,MAAA,UAAgB,OAAA,GAAS,cAAA,GAAsB,aAAa;;AAuDO;;;iBAA3E,cAAA,CAAe,MAAA,UAAgB,OAAA,GAAS,cAAA,GAAsB,aAAa"}
1
+ {"version":3,"file":"transform.d.ts","names":[],"sources":["../src/transform.ts"],"mappings":";;;AAgGoF;AAuDpF;;;;;;;AAvDoF,iBAApE,OAAA,CAAQ,MAAA,UAAgB,OAAA,GAAS,cAAA,GAAsB,aAAa;;AAuDO;;;iBAA3E,cAAA,CAAe,MAAA,UAAgB,OAAA,GAAS,cAAA,GAAsB,aAAa"}
package/dist/transform.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { checkBudget } from "./budget.js";
1
2
  import { createFlattenTransformer } from "./flatten.js";
2
3
  import { hasErrors, typecheck } from "./typecheck.js";
3
4
  import { perfLint } from "./perf-lint.js";
@@ -115,13 +116,24 @@ function compileChecked(source, options = {}) {
115
116
  };
116
117
  const compiled = compile(source, options);
117
118
  const perfDiagnostics = options.perf ? perfLint(source, fileName, typeof options.perf === "object" ? options.perf : {}) : [];
119
+ const budgetDiagnostics = options.budget ? checkBudget(compiled, options.budget).map((d) => ({
120
+ ...d,
121
+ file: fileName
122
+ })) : [];
123
+ const allDiagnostics = [
124
+ ...diagnostics,
125
+ ...perfDiagnostics,
126
+ ...budgetDiagnostics,
127
+ ...compiled.diagnostics
128
+ ];
129
+ if (hasErrors(budgetDiagnostics)) return {
130
+ code: "",
131
+ diagnostics: allDiagnostics,
132
+ stats: compiled.stats
133
+ };
118
134
  return {
119
135
  ...compiled,
120
- diagnostics: [
121
- ...diagnostics,
122
- ...perfDiagnostics,
123
- ...compiled.diagnostics
124
- ]
136
+ diagnostics: allDiagnostics
125
137
  };
126
138
  }
127
139
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"transform.js","names":[],"sources":["../src/transform.ts"],"sourcesContent":["/**\n * The MDC transform/compile pipeline.\n *\n * `compile()` lowers TSX → `createElement(...)` (matching `@mindees/core`'s\n * factory), runs the built-in optimizer passes (tree-flattening) plus any user\n * plugins, and emits JavaScript + a source map. It does **not** type-check\n * (that's {@link typecheck}); `compileChecked()` runs the gate first and refuses\n * to emit on `error` diagnostics.\n *\n * @module\n */\n\nimport ts from 'typescript'\nimport { createFlattenTransformer } from './flatten'\nimport { perfLint } from './perf-lint'\nimport { hasErrors, typecheck } from './typecheck'\nimport type { CompileOptions, CompileResult, CompileStats } from './types'\n\n/** Compiler options for emit (JSX → `createElement`/`Fragment`, which the optimizer matches). */\nfunction emitOptions(sourceMap: boolean): ts.CompilerOptions {\n return {\n jsx: ts.JsxEmit.React,\n jsxFactory: 'createElement',\n jsxFragmentFactory: 'Fragment',\n target: ts.ScriptTarget.ES2023,\n module: ts.ModuleKind.ESNext,\n sourceMap,\n }\n}\n\n/** Runtime names the JSX desugar references; injected from `@mindees/core` if unbound. */\nconst RUNTIME_NAMES = ['createElement', 'Fragment'] as const\n\n/**\n * Ensure the JSX runtime is in scope. Idiomatic components use **automatic JSX** and import\n * nothing, but we emit classic `createElement(...)`/`Fragment` (so the tree-flatten optimizer\n * can match them) — which would be unbound at runtime. This transformer prepends\n * `import { createElement, Fragment } from '@mindees/core'` for any runtime name that is\n * referenced but not already imported, so emitted modules run. Runs LAST (after flatten/plugins),\n * so names the optimizer removed don't get a needless import.\n */\nfunction createRuntimeImportTransformer(tsmod: typeof ts): ts.TransformerFactory<ts.SourceFile> {\n return (context) => (sourceFile) => {\n const imported = new Set<string>()\n for (const stmt of sourceFile.statements) {\n if (\n tsmod.isImportDeclaration(stmt) &&\n tsmod.isStringLiteral(stmt.moduleSpecifier) &&\n stmt.moduleSpecifier.text === '@mindees/core'\n ) {\n const named = stmt.importClause?.namedBindings\n if (named && tsmod.isNamedImports(named)) {\n for (const el of named.elements) imported.add((el.propertyName ?? el.name).text)\n }\n }\n }\n const referenced = new Set<string>()\n const visit = (node: ts.Node): void => {\n if (tsmod.isIdentifier(node) && (RUNTIME_NAMES as readonly string[]).includes(node.text)) {\n referenced.add(node.text)\n }\n tsmod.forEachChild(node, visit)\n }\n visit(sourceFile)\n const missing = RUNTIME_NAMES.filter((n) => referenced.has(n) && !imported.has(n))\n if (missing.length === 0) return sourceFile\n const importDecl = tsmod.factory.createImportDeclaration(\n undefined,\n tsmod.factory.createImportClause(\n false,\n undefined,\n tsmod.factory.createNamedImports(\n missing.map((n) =>\n tsmod.factory.createImportSpecifier(\n false,\n undefined,\n tsmod.factory.createIdentifier(n),\n ),\n ),\n ),\n ),\n tsmod.factory.createStringLiteral('@mindees/core'),\n )\n return context.factory.updateSourceFile(sourceFile, [importDecl, ...sourceFile.statements])\n }\n}\n\n/**\n * Compile a single TSX/TS module to JavaScript.\n *\n * Pipeline: JSX desugar → tree-flatten (optional) → user plugins → emit.\n * Returns emitted code, an optional source map, any (transpile-level)\n * diagnostics, and optimizer stats. Use {@link compileChecked} to gate on the\n * full type checker.\n */\nexport function compile(source: string, options: CompileOptions = {}): CompileResult {\n const { fileName = 'module.tsx', sourceMap = true, flatten = true, plugins = [] } = options\n\n // IMPORTANT: our optimizer + plugins operate on the desugared\n // `createElement(...)` call form, but `transpileModule` runs `before`\n // transformers on the *pre-desugar* JSX AST. JSX is lowered during the\n // `after` phase, so flatten/plugins must run there to see the calls.\n const after: ts.TransformerFactory<ts.SourceFile>[] = []\n let stats: CompileStats = { flattenedNodes: 0, totalElements: 0 }\n\n if (flatten) {\n const flattener = createFlattenTransformer(ts)\n after.push(flattener.factory)\n stats = flattener.stats // live object, updated during emit\n }\n\n for (const plugin of plugins) {\n after.push(plugin.transformer(ts) as ts.TransformerFactory<ts.SourceFile>)\n }\n\n // LAST: bind the JSX runtime (automatic-JSX components import nothing) so output runs.\n after.push(createRuntimeImportTransformer(ts))\n\n const output = ts.transpileModule(source, {\n compilerOptions: emitOptions(sourceMap),\n fileName,\n reportDiagnostics: true,\n transformers: { after },\n })\n\n // transpileModule only surfaces a few syntactic diagnostics; semantic ones\n // come from the type-check gate. Map each to our structured form.\n const diagnostics = (output.diagnostics ?? []).map((d) => {\n const message = ts.flattenDiagnosticMessageText(d.messageText, '\\n')\n return {\n severity:\n d.category === ts.DiagnosticCategory.Error ? ('error' as const) : ('warning' as const),\n code: `TS${d.code}`,\n message,\n }\n })\n\n const result: CompileResult = {\n code: output.outputText,\n diagnostics,\n stats,\n }\n if (sourceMap && output.sourceMapText) result.map = output.sourceMapText\n return result\n}\n\n/**\n * Type-check then compile. If the gate finds any `error` diagnostic, returns it\n * WITHOUT emitting code (`code: ''`) — the build must not ship type errors.\n */\nexport function compileChecked(source: string, options: CompileOptions = {}): CompileResult {\n const fileName = options.fileName ?? 'module.tsx'\n const diagnostics = typecheck(source, fileName)\n if (hasErrors(diagnostics)) {\n return { code: '', diagnostics, stats: { flattenedNodes: 0, totalElements: 0 } }\n }\n const compiled = compile(source, options)\n // Opt-in build-time perf-lint: warnings only (never blocks — the gate above already returned on\n // errors, and every perf diagnostic is severity 'warning').\n const perfDiagnostics = options.perf\n ? perfLint(source, fileName, typeof options.perf === 'object' ? options.perf : {})\n : []\n // Surface type-check warnings + perf warnings alongside the compile result.\n return {\n ...compiled,\n diagnostics: [...diagnostics, ...perfDiagnostics, ...compiled.diagnostics],\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"transform.js","names":[],"sources":["../src/transform.ts"],"sourcesContent":["/**\n * The MDC transform/compile pipeline.\n *\n * `compile()` lowers TSX → `createElement(...)` (matching `@mindees/core`'s\n * factory), runs the built-in optimizer passes (tree-flattening) plus any user\n * plugins, and emits JavaScript + a source map. It does **not** type-check\n * (that's {@link typecheck}); `compileChecked()` runs the gate first and refuses\n * to emit on `error` diagnostics.\n *\n * @module\n */\n\nimport ts from 'typescript'\nimport { checkBudget } from './budget'\nimport { createFlattenTransformer } from './flatten'\nimport { perfLint } from './perf-lint'\nimport { hasErrors, typecheck } from './typecheck'\nimport type { CompileOptions, CompileResult, CompileStats } from './types'\n\n/** Compiler options for emit (JSX → `createElement`/`Fragment`, which the optimizer matches). */\nfunction emitOptions(sourceMap: boolean): ts.CompilerOptions {\n return {\n jsx: ts.JsxEmit.React,\n jsxFactory: 'createElement',\n jsxFragmentFactory: 'Fragment',\n target: ts.ScriptTarget.ES2023,\n module: ts.ModuleKind.ESNext,\n sourceMap,\n }\n}\n\n/** Runtime names the JSX desugar references; injected from `@mindees/core` if unbound. */\nconst RUNTIME_NAMES = ['createElement', 'Fragment'] as const\n\n/**\n * Ensure the JSX runtime is in scope. Idiomatic components use **automatic JSX** and import\n * nothing, but we emit classic `createElement(...)`/`Fragment` (so the tree-flatten optimizer\n * can match them) — which would be unbound at runtime. This transformer prepends\n * `import { createElement, Fragment } from '@mindees/core'` for any runtime name that is\n * referenced but not already imported, so emitted modules run. Runs LAST (after flatten/plugins),\n * so names the optimizer removed don't get a needless import.\n */\nfunction createRuntimeImportTransformer(tsmod: typeof ts): ts.TransformerFactory<ts.SourceFile> {\n return (context) => (sourceFile) => {\n const imported = new Set<string>()\n for (const stmt of sourceFile.statements) {\n if (\n tsmod.isImportDeclaration(stmt) &&\n tsmod.isStringLiteral(stmt.moduleSpecifier) &&\n stmt.moduleSpecifier.text === '@mindees/core'\n ) {\n const named = stmt.importClause?.namedBindings\n if (named && tsmod.isNamedImports(named)) {\n for (const el of named.elements) imported.add((el.propertyName ?? el.name).text)\n }\n }\n }\n const referenced = new Set<string>()\n const visit = (node: ts.Node): void => {\n if (tsmod.isIdentifier(node) && (RUNTIME_NAMES as readonly string[]).includes(node.text)) {\n referenced.add(node.text)\n }\n tsmod.forEachChild(node, visit)\n }\n visit(sourceFile)\n const missing = RUNTIME_NAMES.filter((n) => referenced.has(n) && !imported.has(n))\n if (missing.length === 0) return sourceFile\n const importDecl = tsmod.factory.createImportDeclaration(\n undefined,\n tsmod.factory.createImportClause(\n false,\n undefined,\n tsmod.factory.createNamedImports(\n missing.map((n) =>\n tsmod.factory.createImportSpecifier(\n false,\n undefined,\n tsmod.factory.createIdentifier(n),\n ),\n ),\n ),\n ),\n tsmod.factory.createStringLiteral('@mindees/core'),\n )\n return context.factory.updateSourceFile(sourceFile, [importDecl, ...sourceFile.statements])\n }\n}\n\n/**\n * Compile a single TSX/TS module to JavaScript.\n *\n * Pipeline: JSX desugar → tree-flatten (optional) → user plugins → emit.\n * Returns emitted code, an optional source map, any (transpile-level)\n * diagnostics, and optimizer stats. Use {@link compileChecked} to gate on the\n * full type checker.\n */\nexport function compile(source: string, options: CompileOptions = {}): CompileResult {\n const { fileName = 'module.tsx', sourceMap = true, flatten = true, plugins = [] } = options\n\n // IMPORTANT: our optimizer + plugins operate on the desugared\n // `createElement(...)` call form, but `transpileModule` runs `before`\n // transformers on the *pre-desugar* JSX AST. JSX is lowered during the\n // `after` phase, so flatten/plugins must run there to see the calls.\n const after: ts.TransformerFactory<ts.SourceFile>[] = []\n let stats: CompileStats = { flattenedNodes: 0, totalElements: 0 }\n\n if (flatten) {\n const flattener = createFlattenTransformer(ts)\n after.push(flattener.factory)\n stats = flattener.stats // live object, updated during emit\n }\n\n for (const plugin of plugins) {\n after.push(plugin.transformer(ts) as ts.TransformerFactory<ts.SourceFile>)\n }\n\n // LAST: bind the JSX runtime (automatic-JSX components import nothing) so output runs.\n after.push(createRuntimeImportTransformer(ts))\n\n const output = ts.transpileModule(source, {\n compilerOptions: emitOptions(sourceMap),\n fileName,\n reportDiagnostics: true,\n transformers: { after },\n })\n\n // transpileModule only surfaces a few syntactic diagnostics; semantic ones\n // come from the type-check gate. Map each to our structured form.\n const diagnostics = (output.diagnostics ?? []).map((d) => {\n const message = ts.flattenDiagnosticMessageText(d.messageText, '\\n')\n return {\n severity:\n d.category === ts.DiagnosticCategory.Error ? ('error' as const) : ('warning' as const),\n code: `TS${d.code}`,\n message,\n }\n })\n\n const result: CompileResult = {\n code: output.outputText,\n diagnostics,\n stats,\n }\n if (sourceMap && output.sourceMapText) result.map = output.sourceMapText\n return result\n}\n\n/**\n * Type-check then compile. If the gate finds any `error` diagnostic, returns it\n * WITHOUT emitting code (`code: ''`) — the build must not ship type errors.\n */\nexport function compileChecked(source: string, options: CompileOptions = {}): CompileResult {\n const fileName = options.fileName ?? 'module.tsx'\n const diagnostics = typecheck(source, fileName)\n if (hasErrors(diagnostics)) {\n return { code: '', diagnostics, stats: { flattenedNodes: 0, totalElements: 0 } }\n }\n const compiled = compile(source, options)\n // Opt-in build-time perf-lint: warnings only (never blocks — the gate above already returned on\n // errors, and every perf diagnostic is severity 'warning').\n const perfDiagnostics = options.perf\n ? perfLint(source, fileName, typeof options.perf === 'object' ? options.perf : {})\n : []\n // Performance budget (spec §12): violations are ERRORS that refuse to emit — \"100% optimized,\n // enforced.\" Attach the budget errors to a file so editors surface them.\n const budgetDiagnostics = options.budget\n ? checkBudget(compiled, options.budget).map((d) => ({ ...d, file: fileName }))\n : []\n const allDiagnostics = [\n ...diagnostics,\n ...perfDiagnostics,\n ...budgetDiagnostics,\n ...compiled.diagnostics,\n ]\n if (hasErrors(budgetDiagnostics)) {\n // Over budget → refuse to emit (same contract as the type-check gate above).\n return { code: '', diagnostics: allDiagnostics, stats: compiled.stats }\n }\n return { ...compiled, diagnostics: allDiagnostics }\n}\n"],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { PerfLintOptions } from "./perf-lint.js";
2
+ import { BudgetOptions } from "./budget.js";
2
3
 
3
4
  //#region src/types.d.ts
4
5
  /** Severity of a {@link Diagnostic}. */
@@ -55,6 +56,11 @@ interface CompileOptions {
55
56
  * render footguns; never blocks the build. `true` for defaults, or pass {@link PerfLintOptions}.
56
57
  */
57
58
  perf?: boolean | PerfLintOptions;
59
+ /**
60
+ * Enforce a performance budget (`compileChecked` only): a violation is an **error** that refuses to
61
+ * emit (spec §12 — "100% optimized, enforced"). See {@link BudgetOptions}.
62
+ */
63
+ budget?: BudgetOptions;
58
64
  }
59
65
  /**
60
66
  * A transform plugin. Plugins operate on the desugared `createElement(...)` call
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;;;KASY,kBAAA;AAGZ;AAAA,UAAiB,cAAA;;EAEf,IAAA;EAEM;EAAN,MAAM;AAAA;;UAIS,UAAA;EACf,QAAA,EAAU,kBAAA;EAAA;EAEV,IAAA;EAEA;EAAA,OAAA;EAIA;EAFA,IAAA;EAEyB;EAAzB,QAAA,GAAW,cAAc;AAAA;;UAIV,aAAA;EAQI;EANnB,IAAA;EAEA;EAAA,GAAA;EAEa;EAAb,WAAA,EAAa,UAAA;EAEN;EAAP,KAAA,EAAO,YAAY;AAAA;AAIrB;AAAA,UAAiB,YAAA;;EAEf,cAAA;EAEa;EAAb,aAAa;AAAA;;UAIE,cAAA;EAEf;EAAA,QAAA;EAIA;EAFA,SAAA;EAIU;EAFV,OAAA;EAOiB;EALjB,OAAA,GAAU,SAAA;EAKsB;AAYlC;;;EAZE,IAAA,aAAiB,eAAe;AAAA;;;;AAoBa;;;;;;UAR9B,SAAA;;EAEf,IAAA;;;;;;EAMA,WAAA,GAAc,EAAA;AAAA"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;KAUY,kBAAA;AAGZ;AAAA,UAAiB,cAAA;;EAEf,IAAA;EAEM;EAAN,MAAM;AAAA;;UAIS,UAAA;EACf,QAAA,EAAU,kBAAA;EAAA;EAEV,IAAA;EAEA;EAAA,OAAA;EAIA;EAFA,IAAA;EAEyB;EAAzB,QAAA,GAAW,cAAc;AAAA;;UAIV,aAAA;EAQI;EANnB,IAAA;EAEA;EAAA,GAAA;EAEa;EAAb,WAAA,EAAa,UAAA;EAEN;EAAP,KAAA,EAAO,YAAY;AAAA;AAIrB;AAAA,UAAiB,YAAA;;EAEf,cAAA;EAEa;EAAb,aAAa;AAAA;;UAIE,cAAA;EAaE;EAXjB,QAAA;EAgBsB;EAdtB,SAAA;EAFA;EAIA,OAAA;EAAA;EAEA,OAAA,GAAU,SAAA;EAAA;;;;EAKV,IAAA,aAAiB,eAAA;EAKK;AAAA;AAYxB;;EAZE,MAAA,GAAS,aAAA;AAAA;;;;;AAoBoC;;;;;UAR9B,SAAA;;EAEf,IAAA;;;;;;EAMA,WAAA,GAAc,EAAA;AAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindees/compiler",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "MindeesNative Compiler (MDC) — build-time optimizer: type-check gate, TSX→createElement transform, tree-flattening, per-route code-splitting, and a plugin API. TS→native AOT is a research track.",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "type": "module",
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "typescript": "6.0.3",
27
- "@mindees/core": "0.9.0"
27
+ "@mindees/core": "0.11.0"
28
28
  },
29
29
  "scripts": {
30
30
  "build": "tsdown",