@mindees/compiler 0.17.0 → 0.20.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.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/perf-lint.js +7 -0
- package/dist/perf-lint.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { Maturity, NotImplementedError, PackageInfo, notImplemented } from "@min
|
|
|
12
12
|
/** The npm package name. */
|
|
13
13
|
declare const name = "@mindees/compiler";
|
|
14
14
|
/** The package version. All `@mindees/*` packages share one locked version line. */
|
|
15
|
-
declare const VERSION = "0.
|
|
15
|
+
declare const VERSION = "0.20.0";
|
|
16
16
|
/**
|
|
17
17
|
* Current maturity. The build-time optimizer — type-check gate, TSX→createElement
|
|
18
18
|
* transform, tree-flattening, per-route manifest, plugin API — is implemented
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { NotImplementedError, notImplemented } from "@mindees/core";
|
|
|
10
10
|
/** The npm package name. */
|
|
11
11
|
const name = "@mindees/compiler";
|
|
12
12
|
/** The package version. All `@mindees/*` packages share one locked version line. */
|
|
13
|
-
const VERSION = "0.
|
|
13
|
+
const VERSION = "0.20.0";
|
|
14
14
|
/**
|
|
15
15
|
* Current maturity. The build-time optimizer — type-check gate, TSX→createElement
|
|
16
16
|
* transform, tree-flattening, per-route manifest, plugin API — is implemented
|
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/** 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.
|
|
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.20.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"}
|
package/dist/perf-lint.js
CHANGED
|
@@ -243,6 +243,12 @@ function perfLint(source, fileName = "module.tsx", options = {}) {
|
|
|
243
243
|
look(fn.body);
|
|
244
244
|
if (subscribes && !hasCleanup) emit(node, "MDC_PERF_005", "effect() subscribes to an external source but returns no cleanup — the listener leaks when the scope disposes. Return a teardown function or call onCleanup(() => …).");
|
|
245
245
|
};
|
|
246
|
+
const ruleAsyncEffect = (node) => {
|
|
247
|
+
if (!ts.isCallExpression(node) || calleeName(node) !== "effect") return;
|
|
248
|
+
const fn = node.arguments[0];
|
|
249
|
+
if (!fn || !ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn)) return;
|
|
250
|
+
if (fn.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false) emit(node, "MDC_PERF_008", "effect() given an async function — dependency tracking stops at the first `await` (signals written afterward won't re-run the effect), and the returned Promise is ignored, not used as cleanup. Keep the effect sync and launch the async work inside it: effect(() => { const d = dep(); void run(d) }).");
|
|
251
|
+
};
|
|
246
252
|
const ruleConstFunctionStyle = (node) => {
|
|
247
253
|
if (!ts.isJsxAttribute(node) || !node.initializer) return;
|
|
248
254
|
if (!ts.isJsxExpression(node.initializer) || !node.initializer.expression) return;
|
|
@@ -266,6 +272,7 @@ function perfLint(source, fileName = "module.tsx", options = {}) {
|
|
|
266
272
|
ruleMapJsxChild(node);
|
|
267
273
|
ruleMissingKey(node);
|
|
268
274
|
ruleHeavyEffect(node);
|
|
275
|
+
ruleAsyncEffect(node);
|
|
269
276
|
ruleRepeatedReadInLoop(node);
|
|
270
277
|
ruleEffectNoCleanup(node);
|
|
271
278
|
ruleConstFunctionStyle(node);
|
package/dist/perf-lint.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"perf-lint.js","names":[],"sources":["../src/perf-lint.ts"],"sourcesContent":["/**\n * Build-time **perf-lint** — an opt-in pass that flags real performance footguns in the\n * fine-grained reactive + Helix render model, as `warning` {@link Diagnostic}s (it NEVER blocks the\n * build). Honest by design: every rule reports a concrete structural fact and *why* it's slow in\n * THIS model — no invented frame-time numbers. A diagnostic neither React Native nor Flutter ships.\n *\n * Enable via `compileChecked(src, { perf: true })` (or `{ perf: { rules, listSizeThreshold } }`).\n * Suppress a finding with a leading `// mdc-perf-ignore` (all) or `// mdc-perf-ignore MDC_PERF_001`\n * (one code) comment, or `rules: { MDC_PERF_001: 'off' }`.\n *\n * @module\n */\n\nimport ts from 'typescript'\nimport { scriptKindForFile } from './typecheck'\nimport type { Diagnostic } from './types'\n\n/** Per-call configuration for {@link perfLint}. */\nexport interface PerfLintOptions {\n /** Turn individual rules on/off (default: all on except `MDC_PERF_007`). */\n readonly rules?: Record<string, 'off' | 'warning'>\n /** Element count at/above which `MDC_PERF_007` fires (default 50). */\n readonly listSizeThreshold?: number\n /** Identifiers treated as keyed list builders (default For/keyedRegion/List/createList/SectionList/createSectionList). */\n readonly keyedNames?: readonly string[]\n}\n\nconst DEFAULT_KEYED = [\n 'For',\n 'keyedRegion',\n 'List',\n 'createList',\n 'SectionList',\n 'createSectionList',\n]\nconst OFF_BY_DEFAULT = new Set(['MDC_PERF_007'])\n/** A `const x = signal()|computed()|memo()|use<Capital>()` binds a reactive accessor. */\nconst ACCESSOR_FACTORY = /^(signal|computed|memo|use[A-Z])/\nconst HEAVY_METHODS = new Set([\n 'map',\n 'filter',\n 'reduce',\n 'forEach',\n 'sort',\n 'flatMap',\n 'reduceRight',\n])\nconst SUBSCRIBE_CALLS =\n /(addEventListener|setInterval|setTimeout|subscribe|^on$|observe|addListener)/\n\n/** Run the perf-lint over one module's source; returns `warning` diagnostics (possibly empty). */\nexport function perfLint(\n source: string,\n fileName = 'module.tsx',\n options: PerfLintOptions = {},\n): Diagnostic[] {\n const sf = ts.createSourceFile(\n fileName,\n source,\n ts.ScriptTarget.ES2023,\n /* setParentNodes */ true,\n scriptKindForFile(fileName),\n )\n const keyed = new Set(options.keyedNames ?? DEFAULT_KEYED)\n const threshold = options.listSizeThreshold ?? 50\n const out: Diagnostic[] = []\n\n const isEnabled = (code: string): boolean => {\n const setting = options.rules?.[code]\n if (setting) return setting !== 'off'\n return !OFF_BY_DEFAULT.has(code)\n }\n\n // --- pre-pass: the set of identifiers bound to reactive accessors (signal/computed/memo/use*) ---\n const accessors = new Set<string>()\n const addParamName = (param: ts.ParameterDeclaration | undefined): void => {\n if (param && ts.isIdentifier(param.name)) accessors.add(param.name.text)\n }\n const collectAccessors = (node: ts.Node): void => {\n if (\n ts.isVariableDeclaration(node) &&\n ts.isIdentifier(node.name) &&\n node.initializer &&\n ts.isCallExpression(node.initializer)\n ) {\n const callee = node.initializer.expression\n const name = ts.isIdentifier(callee)\n ? callee.text\n : ts.isPropertyAccessExpression(callee)\n ? callee.name.text\n : ''\n if (ACCESSOR_FACTORY.test(name)) accessors.add(node.name.text)\n }\n // A keyed builder's row callback receives accessors: For/List({ children|renderItem: (item, index) => … })\n // where item()/index() are reads. Seed those param names so rules 003/004 see them.\n if (\n ts.isCallExpression(node) &&\n keyed.has(ts.isIdentifier(node.expression) ? node.expression.text : '')\n ) {\n const arg = node.arguments[0]\n if (arg && ts.isObjectLiteralExpression(arg)) {\n for (const p of arg.properties) {\n if (\n ts.isPropertyAssignment(p) &&\n ts.isIdentifier(p.name) &&\n (p.name.text === 'children' || p.name.text === 'renderItem') &&\n (ts.isArrowFunction(p.initializer) || ts.isFunctionExpression(p.initializer))\n ) {\n addParamName(p.initializer.parameters[0]) // item accessor\n addParamName(p.initializer.parameters[1]) // index accessor\n }\n }\n }\n }\n ts.forEachChild(node, collectAccessors)\n }\n collectAccessors(sf)\n\n const positionOf = (node: ts.Node) => {\n const { line, character } = sf.getLineAndCharacterOfPosition(node.getStart(sf))\n return { line: line + 1, column: character + 1 }\n }\n // Line-based suppression: an `mdc-perf-ignore [CODE]` comment on the finding's line or the line\n // above. This works uniformly for `//` comments AND JSX `{/* … */}` comment children (whose comment\n // text isn't in the flagged node's leading trivia).\n const sourceLines = source.split('\\n')\n const isSuppressed = (node: ts.Node, code: string): boolean => {\n const { line } = sf.getLineAndCharacterOfPosition(node.getStart(sf))\n for (const ln of [line, line - 1]) {\n const m = /mdc-perf-ignore(?:\\s+(MDC_PERF_\\d+))?/.exec(sourceLines[ln] ?? '')\n if (m && (!m[1] || m[1] === code)) return true\n }\n return false\n }\n const emit = (node: ts.Node, code: string, message: string): void => {\n if (!isEnabled(code) || isSuppressed(node, code)) return\n out.push({ severity: 'warning', code, message, file: fileName, position: positionOf(node) })\n }\n\n // --- shared AST helpers ---\n const unwrapArrow = (expr: ts.Expression): ts.Expression => {\n // `() => X` → X (the arrow's expression body), else the node itself.\n if (ts.isArrowFunction(expr) && !ts.isBlock(expr.body)) return expr.body\n return expr\n }\n const returnsJsx = (fn: ts.Expression): boolean => {\n if (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn)) return false\n let found = false\n const look = (n: ts.Node): void => {\n if (found) return\n if (ts.isJsxElement(n) || ts.isJsxFragment(n) || ts.isJsxSelfClosingElement(n)) {\n found = true\n return\n }\n // don't descend into a NESTED function (its JSX isn't this callback's return)\n if (\n n !== fn &&\n (ts.isArrowFunction(n) || ts.isFunctionExpression(n) || ts.isFunctionDeclaration(n))\n )\n return\n ts.forEachChild(n, look)\n }\n look(fn.body)\n return found\n }\n const calleeName = (call: ts.CallExpression): string =>\n ts.isIdentifier(call.expression) ? call.expression.text : ''\n const propAccessName = (call: ts.CallExpression): string =>\n ts.isPropertyAccessExpression(call.expression) ? call.expression.name.text : ''\n /** Does the subtree contain ANY call expression? (A returned literal with no call is provably static.) */\n const containsAnyCall = (root: ts.Node): boolean => {\n let found = false\n const look = (n: ts.Node): void => {\n if (found) return\n if (ts.isCallExpression(n)) {\n found = true\n return\n }\n ts.forEachChild(n, look)\n }\n look(root)\n return found\n }\n /** Does the subtree reactively read a known accessor (a zero-arg call `acc()`)? */\n const readsAccessor = (root: ts.Node): boolean => {\n let found = false\n const look = (n: ts.Node): void => {\n if (found) return\n if (\n ts.isCallExpression(n) &&\n n.arguments.length === 0 &&\n ts.isIdentifier(n.expression) &&\n accessors.has(n.expression.text)\n ) {\n found = true\n return\n }\n ts.forEachChild(n, look)\n }\n look(root)\n return found\n }\n /** Does the subtree contain a heavy synchronous construct (loop / array-method chain / JSON)? */\n const hasHeavyWork = (root: ts.Node): boolean => {\n let found = false\n const look = (n: ts.Node): void => {\n if (found) return\n if (\n ts.isForStatement(n) ||\n ts.isForOfStatement(n) ||\n ts.isForInStatement(n) ||\n ts.isWhileStatement(n)\n ) {\n found = true\n return\n }\n if (ts.isCallExpression(n) && HEAVY_METHODS.has(propAccessName(n))) {\n found = true\n return\n }\n if (\n ts.isCallExpression(n) &&\n ts.isPropertyAccessExpression(n.expression) &&\n ts.isIdentifier(n.expression.expression) &&\n n.expression.expression.text === 'JSON'\n ) {\n found = true\n return\n }\n ts.forEachChild(n, look)\n }\n look(root)\n return found\n }\n\n // --- rule matchers ---\n const ruleMapJsxChild = (node: ts.Node): void => {\n if (!ts.isJsxExpression(node) || !node.expression) return\n const parent = node.parent\n if (!parent || !(ts.isJsxElement(parent) || ts.isJsxFragment(parent))) return\n const expr = unwrapArrow(node.expression)\n if (!ts.isCallExpression(expr) || propAccessName(expr) !== 'map') return\n const cb = expr.arguments[0]\n if (!cb || !returnsJsx(cb)) return\n emit(\n node,\n 'MDC_PERF_001',\n 'List rendered with a bare .map() — on any change this region re-mounts EVERY row ' +\n '(losing focus/scroll/state). Use For({ each, key, children }) (keyed: only the diff is ' +\n 'created/moved/disposed) or List({...}) for large lists.',\n )\n }\n\n const ruleMissingKey = (node: ts.Node): void => {\n if (!ts.isCallExpression(node)) return\n const name = calleeName(node)\n if (!keyed.has(name)) return\n const arg = node.arguments[0]\n if (!arg || !ts.isObjectLiteralExpression(arg)) return\n let hasKey = false\n let hasSpread = false\n let hasEach = false\n for (const p of arg.properties) {\n if (ts.isSpreadAssignment(p)) hasSpread = true\n if (\n (ts.isPropertyAssignment(p) || ts.isShorthandPropertyAssignment(p)) &&\n p.name &&\n ts.isIdentifier(p.name)\n ) {\n if (p.name.text === 'key') hasKey = true\n if (p.name.text === 'each') hasEach = true\n }\n }\n // Only the `{ each, children }` keyed-region shape (For/keyedRegion) defaults to identity keying;\n // List/createList key differently (e.g. keyExtractor) and use a different option shape — requiring\n // `each` avoids false-positives on those.\n if (hasKey || hasSpread || !hasEach) return\n emit(\n node,\n 'MDC_PERF_002',\n `${name}() has no \\`key\\` — it defaults to object identity, so freshly-built rows never ` +\n 'match and every row is disposed + recreated (or throws on a duplicate primitive). Add ' +\n 'key: (item) => item.id (a stable id).',\n )\n }\n\n const ruleHeavyEffect = (node: ts.Node): void => {\n if (!ts.isCallExpression(node) || calleeName(node) !== 'effect') return\n // effect(fn, { priority: 'normal' }) is already deferred — exempt.\n if (node.arguments.length >= 2) return\n const fn = node.arguments[0]\n if (!fn || (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn))) return\n if (hasHeavyWork(fn.body) && readsAccessor(fn.body)) {\n emit(\n node,\n 'MDC_PERF_003',\n 'Heavy synchronous work in a default (sync-lane) effect — it runs inline on every ' +\n 'dependency write, blocking the interaction. Move derived values into computed()/memo() ' +\n \"(lazy + cached), or run heavy work on the deferred lane: effect(fn, { priority: 'normal' }).\",\n )\n }\n }\n\n const ruleRepeatedReadInLoop = (node: ts.Node): void => {\n let body: ts.Node | undefined\n if (\n ts.isForStatement(node) ||\n ts.isForOfStatement(node) ||\n ts.isForInStatement(node) ||\n ts.isWhileStatement(node)\n ) {\n body = node.statement\n } else if (\n ts.isCallExpression(node) &&\n (propAccessName(node) === 'map' || propAccessName(node) === 'forEach')\n ) {\n const cb = node.arguments[0]\n if (cb && (ts.isArrowFunction(cb) || ts.isFunctionExpression(cb))) body = cb.body\n }\n if (!body) return\n // Count zero-arg reads of each known accessor inside the loop body. A nested function resets the\n // scope (its reads run later, not per-iteration), so don't descend into one.\n const occ = new Map<string, { count: number; first: ts.Node }>()\n const walk = (n: ts.Node): void => {\n if (\n n !== body &&\n (ts.isArrowFunction(n) || ts.isFunctionExpression(n) || ts.isFunctionDeclaration(n))\n )\n return\n if (\n ts.isCallExpression(n) &&\n n.arguments.length === 0 &&\n ts.isIdentifier(n.expression) &&\n accessors.has(n.expression.text)\n ) {\n const id = n.expression.text\n const e = occ.get(id)\n if (e) e.count++\n else occ.set(id, { count: 1, first: n })\n }\n ts.forEachChild(n, walk)\n }\n walk(body)\n for (const [id, e] of occ) {\n if (e.count >= 2) {\n emit(\n e.first,\n 'MDC_PERF_004',\n `\\`${id}()\\` is read ${e.count}× inside a loop — each call re-reads the signal. Hoist ` +\n `it once above the loop: const v = ${id}().`,\n )\n }\n }\n }\n\n const ruleEffectNoCleanup = (node: ts.Node): void => {\n if (!ts.isCallExpression(node) || calleeName(node) !== 'effect') return\n const fn = node.arguments[0]\n if (!fn || (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn))) return\n let subscribes = false\n let hasCleanup = false\n const look = (n: ts.Node): void => {\n if (ts.isCallExpression(n)) {\n const nm = ts.isIdentifier(n.expression) ? n.expression.text : propAccessName(n)\n if (SUBSCRIBE_CALLS.test(nm)) subscribes = true\n if (ts.isIdentifier(n.expression) && n.expression.text === 'onCleanup') hasCleanup = true\n }\n // A `return <function>` OR `return <identifier>` is a teardown: effect() registers ANY returned\n // function value as cleanup (reactive.ts), so `const t = …; return t` / `return unsub` count.\n if (\n ts.isReturnStatement(n) &&\n n.expression &&\n (ts.isArrowFunction(n.expression) ||\n ts.isFunctionExpression(n.expression) ||\n ts.isIdentifier(n.expression))\n )\n hasCleanup = true\n ts.forEachChild(n, look)\n }\n look(fn.body)\n if (subscribes && !hasCleanup) {\n emit(\n node,\n 'MDC_PERF_005',\n 'effect() subscribes to an external source but returns no cleanup — the listener leaks ' +\n 'when the scope disposes. Return a teardown function or call onCleanup(() => …).',\n )\n }\n }\n\n const ruleConstFunctionStyle = (node: ts.Node): void => {\n if (!ts.isJsxAttribute(node) || !node.initializer) return\n if (!ts.isJsxExpression(node.initializer) || !node.initializer.expression) return\n const expr = node.initializer.expression\n if (!ts.isArrowFunction(expr) || ts.isBlock(expr.body)) return\n let body: ts.Expression = expr.body\n while (ts.isParenthesizedExpression(body)) body = body.expression // `() => ({...})` parens\n if (!ts.isObjectLiteralExpression(body) && !ts.isArrayLiteralExpression(body)) return\n // Only flag a PROVABLY-STATIC literal — one with NO call expression at all. A body that calls\n // anything (theme(), props.active(), select()/destructured/param accessors, helpers) may read a\n // live signal; flagging it and \"passing the literal directly\" would BREAK reactivity. Erring to\n // silence here keeps the rule from firing on the #1 styled-component pattern.\n if (!containsAnyCall(body)) {\n emit(\n node,\n 'MDC_PERF_006',\n 'A function-valued prop that returns a constant object/array (no reads) allocates a reactive ' +\n 'binding for a value that never changes. Pass the literal directly (style={{…}}).',\n )\n }\n }\n\n const ruleLargeLiteralList = (node: ts.Node): void => {\n if (ts.isJsxElement(node) || ts.isJsxFragment(node)) {\n const count = node.children.filter(\n (c) => ts.isJsxElement(c) || ts.isJsxSelfClosingElement(c) || ts.isJsxFragment(c),\n ).length\n if (count >= threshold)\n emit(\n node,\n 'MDC_PERF_007',\n `${count} static child elements rendered inline — consider List({...}) virtualization for large lists.`,\n )\n } else if (ts.isArrayLiteralExpression(node)) {\n const count = node.elements.filter(\n (c) => ts.isJsxElement(c) || ts.isJsxSelfClosingElement(c) || ts.isJsxFragment(c),\n ).length\n if (count >= threshold)\n emit(\n node,\n 'MDC_PERF_007',\n `${count} JSX elements in an array literal — consider For/List for large lists.`,\n )\n }\n }\n\n const visit = (node: ts.Node): void => {\n ruleMapJsxChild(node)\n ruleMissingKey(node)\n ruleHeavyEffect(node)\n ruleRepeatedReadInLoop(node)\n ruleEffectNoCleanup(node)\n ruleConstFunctionStyle(node)\n ruleLargeLiteralList(node)\n ts.forEachChild(node, visit)\n }\n visit(sf)\n return out\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"perf-lint.js","names":[],"sources":["../src/perf-lint.ts"],"sourcesContent":["/**\n * Build-time **perf-lint** — an opt-in pass that flags real performance footguns in the\n * fine-grained reactive + Helix render model, as `warning` {@link Diagnostic}s (it NEVER blocks the\n * build). Honest by design: every rule reports a concrete structural fact and *why* it's slow in\n * THIS model — no invented frame-time numbers. A diagnostic neither React Native nor Flutter ships.\n *\n * Enable via `compileChecked(src, { perf: true })` (or `{ perf: { rules, listSizeThreshold } }`).\n * Suppress a finding with a leading `// mdc-perf-ignore` (all) or `// mdc-perf-ignore MDC_PERF_001`\n * (one code) comment, or `rules: { MDC_PERF_001: 'off' }`.\n *\n * @module\n */\n\nimport ts from 'typescript'\nimport { scriptKindForFile } from './typecheck'\nimport type { Diagnostic } from './types'\n\n/** Per-call configuration for {@link perfLint}. */\nexport interface PerfLintOptions {\n /** Turn individual rules on/off (default: all on except `MDC_PERF_007`). */\n readonly rules?: Record<string, 'off' | 'warning'>\n /** Element count at/above which `MDC_PERF_007` fires (default 50). */\n readonly listSizeThreshold?: number\n /** Identifiers treated as keyed list builders (default For/keyedRegion/List/createList/SectionList/createSectionList). */\n readonly keyedNames?: readonly string[]\n}\n\nconst DEFAULT_KEYED = [\n 'For',\n 'keyedRegion',\n 'List',\n 'createList',\n 'SectionList',\n 'createSectionList',\n]\nconst OFF_BY_DEFAULT = new Set(['MDC_PERF_007'])\n/** A `const x = signal()|computed()|memo()|use<Capital>()` binds a reactive accessor. */\nconst ACCESSOR_FACTORY = /^(signal|computed|memo|use[A-Z])/\nconst HEAVY_METHODS = new Set([\n 'map',\n 'filter',\n 'reduce',\n 'forEach',\n 'sort',\n 'flatMap',\n 'reduceRight',\n])\nconst SUBSCRIBE_CALLS =\n /(addEventListener|setInterval|setTimeout|subscribe|^on$|observe|addListener)/\n\n/** Run the perf-lint over one module's source; returns `warning` diagnostics (possibly empty). */\nexport function perfLint(\n source: string,\n fileName = 'module.tsx',\n options: PerfLintOptions = {},\n): Diagnostic[] {\n const sf = ts.createSourceFile(\n fileName,\n source,\n ts.ScriptTarget.ES2023,\n /* setParentNodes */ true,\n scriptKindForFile(fileName),\n )\n const keyed = new Set(options.keyedNames ?? DEFAULT_KEYED)\n const threshold = options.listSizeThreshold ?? 50\n const out: Diagnostic[] = []\n\n const isEnabled = (code: string): boolean => {\n const setting = options.rules?.[code]\n if (setting) return setting !== 'off'\n return !OFF_BY_DEFAULT.has(code)\n }\n\n // --- pre-pass: the set of identifiers bound to reactive accessors (signal/computed/memo/use*) ---\n const accessors = new Set<string>()\n const addParamName = (param: ts.ParameterDeclaration | undefined): void => {\n if (param && ts.isIdentifier(param.name)) accessors.add(param.name.text)\n }\n const collectAccessors = (node: ts.Node): void => {\n if (\n ts.isVariableDeclaration(node) &&\n ts.isIdentifier(node.name) &&\n node.initializer &&\n ts.isCallExpression(node.initializer)\n ) {\n const callee = node.initializer.expression\n const name = ts.isIdentifier(callee)\n ? callee.text\n : ts.isPropertyAccessExpression(callee)\n ? callee.name.text\n : ''\n if (ACCESSOR_FACTORY.test(name)) accessors.add(node.name.text)\n }\n // A keyed builder's row callback receives accessors: For/List({ children|renderItem: (item, index) => … })\n // where item()/index() are reads. Seed those param names so rules 003/004 see them.\n if (\n ts.isCallExpression(node) &&\n keyed.has(ts.isIdentifier(node.expression) ? node.expression.text : '')\n ) {\n const arg = node.arguments[0]\n if (arg && ts.isObjectLiteralExpression(arg)) {\n for (const p of arg.properties) {\n if (\n ts.isPropertyAssignment(p) &&\n ts.isIdentifier(p.name) &&\n (p.name.text === 'children' || p.name.text === 'renderItem') &&\n (ts.isArrowFunction(p.initializer) || ts.isFunctionExpression(p.initializer))\n ) {\n addParamName(p.initializer.parameters[0]) // item accessor\n addParamName(p.initializer.parameters[1]) // index accessor\n }\n }\n }\n }\n ts.forEachChild(node, collectAccessors)\n }\n collectAccessors(sf)\n\n const positionOf = (node: ts.Node) => {\n const { line, character } = sf.getLineAndCharacterOfPosition(node.getStart(sf))\n return { line: line + 1, column: character + 1 }\n }\n // Line-based suppression: an `mdc-perf-ignore [CODE]` comment on the finding's line or the line\n // above. This works uniformly for `//` comments AND JSX `{/* … */}` comment children (whose comment\n // text isn't in the flagged node's leading trivia).\n const sourceLines = source.split('\\n')\n const isSuppressed = (node: ts.Node, code: string): boolean => {\n const { line } = sf.getLineAndCharacterOfPosition(node.getStart(sf))\n for (const ln of [line, line - 1]) {\n const m = /mdc-perf-ignore(?:\\s+(MDC_PERF_\\d+))?/.exec(sourceLines[ln] ?? '')\n if (m && (!m[1] || m[1] === code)) return true\n }\n return false\n }\n const emit = (node: ts.Node, code: string, message: string): void => {\n if (!isEnabled(code) || isSuppressed(node, code)) return\n out.push({ severity: 'warning', code, message, file: fileName, position: positionOf(node) })\n }\n\n // --- shared AST helpers ---\n const unwrapArrow = (expr: ts.Expression): ts.Expression => {\n // `() => X` → X (the arrow's expression body), else the node itself.\n if (ts.isArrowFunction(expr) && !ts.isBlock(expr.body)) return expr.body\n return expr\n }\n const returnsJsx = (fn: ts.Expression): boolean => {\n if (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn)) return false\n let found = false\n const look = (n: ts.Node): void => {\n if (found) return\n if (ts.isJsxElement(n) || ts.isJsxFragment(n) || ts.isJsxSelfClosingElement(n)) {\n found = true\n return\n }\n // don't descend into a NESTED function (its JSX isn't this callback's return)\n if (\n n !== fn &&\n (ts.isArrowFunction(n) || ts.isFunctionExpression(n) || ts.isFunctionDeclaration(n))\n )\n return\n ts.forEachChild(n, look)\n }\n look(fn.body)\n return found\n }\n const calleeName = (call: ts.CallExpression): string =>\n ts.isIdentifier(call.expression) ? call.expression.text : ''\n const propAccessName = (call: ts.CallExpression): string =>\n ts.isPropertyAccessExpression(call.expression) ? call.expression.name.text : ''\n /** Does the subtree contain ANY call expression? (A returned literal with no call is provably static.) */\n const containsAnyCall = (root: ts.Node): boolean => {\n let found = false\n const look = (n: ts.Node): void => {\n if (found) return\n if (ts.isCallExpression(n)) {\n found = true\n return\n }\n ts.forEachChild(n, look)\n }\n look(root)\n return found\n }\n /** Does the subtree reactively read a known accessor (a zero-arg call `acc()`)? */\n const readsAccessor = (root: ts.Node): boolean => {\n let found = false\n const look = (n: ts.Node): void => {\n if (found) return\n if (\n ts.isCallExpression(n) &&\n n.arguments.length === 0 &&\n ts.isIdentifier(n.expression) &&\n accessors.has(n.expression.text)\n ) {\n found = true\n return\n }\n ts.forEachChild(n, look)\n }\n look(root)\n return found\n }\n /** Does the subtree contain a heavy synchronous construct (loop / array-method chain / JSON)? */\n const hasHeavyWork = (root: ts.Node): boolean => {\n let found = false\n const look = (n: ts.Node): void => {\n if (found) return\n if (\n ts.isForStatement(n) ||\n ts.isForOfStatement(n) ||\n ts.isForInStatement(n) ||\n ts.isWhileStatement(n)\n ) {\n found = true\n return\n }\n if (ts.isCallExpression(n) && HEAVY_METHODS.has(propAccessName(n))) {\n found = true\n return\n }\n if (\n ts.isCallExpression(n) &&\n ts.isPropertyAccessExpression(n.expression) &&\n ts.isIdentifier(n.expression.expression) &&\n n.expression.expression.text === 'JSON'\n ) {\n found = true\n return\n }\n ts.forEachChild(n, look)\n }\n look(root)\n return found\n }\n\n // --- rule matchers ---\n const ruleMapJsxChild = (node: ts.Node): void => {\n if (!ts.isJsxExpression(node) || !node.expression) return\n const parent = node.parent\n if (!parent || !(ts.isJsxElement(parent) || ts.isJsxFragment(parent))) return\n const expr = unwrapArrow(node.expression)\n if (!ts.isCallExpression(expr) || propAccessName(expr) !== 'map') return\n const cb = expr.arguments[0]\n if (!cb || !returnsJsx(cb)) return\n emit(\n node,\n 'MDC_PERF_001',\n 'List rendered with a bare .map() — on any change this region re-mounts EVERY row ' +\n '(losing focus/scroll/state). Use For({ each, key, children }) (keyed: only the diff is ' +\n 'created/moved/disposed) or List({...}) for large lists.',\n )\n }\n\n const ruleMissingKey = (node: ts.Node): void => {\n if (!ts.isCallExpression(node)) return\n const name = calleeName(node)\n if (!keyed.has(name)) return\n const arg = node.arguments[0]\n if (!arg || !ts.isObjectLiteralExpression(arg)) return\n let hasKey = false\n let hasSpread = false\n let hasEach = false\n for (const p of arg.properties) {\n if (ts.isSpreadAssignment(p)) hasSpread = true\n if (\n (ts.isPropertyAssignment(p) || ts.isShorthandPropertyAssignment(p)) &&\n p.name &&\n ts.isIdentifier(p.name)\n ) {\n if (p.name.text === 'key') hasKey = true\n if (p.name.text === 'each') hasEach = true\n }\n }\n // Only the `{ each, children }` keyed-region shape (For/keyedRegion) defaults to identity keying;\n // List/createList key differently (e.g. keyExtractor) and use a different option shape — requiring\n // `each` avoids false-positives on those.\n if (hasKey || hasSpread || !hasEach) return\n emit(\n node,\n 'MDC_PERF_002',\n `${name}() has no \\`key\\` — it defaults to object identity, so freshly-built rows never ` +\n 'match and every row is disposed + recreated (or throws on a duplicate primitive). Add ' +\n 'key: (item) => item.id (a stable id).',\n )\n }\n\n const ruleHeavyEffect = (node: ts.Node): void => {\n if (!ts.isCallExpression(node) || calleeName(node) !== 'effect') return\n // effect(fn, { priority: 'normal' }) is already deferred — exempt.\n if (node.arguments.length >= 2) return\n const fn = node.arguments[0]\n if (!fn || (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn))) return\n if (hasHeavyWork(fn.body) && readsAccessor(fn.body)) {\n emit(\n node,\n 'MDC_PERF_003',\n 'Heavy synchronous work in a default (sync-lane) effect — it runs inline on every ' +\n 'dependency write, blocking the interaction. Move derived values into computed()/memo() ' +\n \"(lazy + cached), or run heavy work on the deferred lane: effect(fn, { priority: 'normal' }).\",\n )\n }\n }\n\n const ruleRepeatedReadInLoop = (node: ts.Node): void => {\n let body: ts.Node | undefined\n if (\n ts.isForStatement(node) ||\n ts.isForOfStatement(node) ||\n ts.isForInStatement(node) ||\n ts.isWhileStatement(node)\n ) {\n body = node.statement\n } else if (\n ts.isCallExpression(node) &&\n (propAccessName(node) === 'map' || propAccessName(node) === 'forEach')\n ) {\n const cb = node.arguments[0]\n if (cb && (ts.isArrowFunction(cb) || ts.isFunctionExpression(cb))) body = cb.body\n }\n if (!body) return\n // Count zero-arg reads of each known accessor inside the loop body. A nested function resets the\n // scope (its reads run later, not per-iteration), so don't descend into one.\n const occ = new Map<string, { count: number; first: ts.Node }>()\n const walk = (n: ts.Node): void => {\n if (\n n !== body &&\n (ts.isArrowFunction(n) || ts.isFunctionExpression(n) || ts.isFunctionDeclaration(n))\n )\n return\n if (\n ts.isCallExpression(n) &&\n n.arguments.length === 0 &&\n ts.isIdentifier(n.expression) &&\n accessors.has(n.expression.text)\n ) {\n const id = n.expression.text\n const e = occ.get(id)\n if (e) e.count++\n else occ.set(id, { count: 1, first: n })\n }\n ts.forEachChild(n, walk)\n }\n walk(body)\n for (const [id, e] of occ) {\n if (e.count >= 2) {\n emit(\n e.first,\n 'MDC_PERF_004',\n `\\`${id}()\\` is read ${e.count}× inside a loop — each call re-reads the signal. Hoist ` +\n `it once above the loop: const v = ${id}().`,\n )\n }\n }\n }\n\n const ruleEffectNoCleanup = (node: ts.Node): void => {\n if (!ts.isCallExpression(node) || calleeName(node) !== 'effect') return\n const fn = node.arguments[0]\n if (!fn || (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn))) return\n let subscribes = false\n let hasCleanup = false\n const look = (n: ts.Node): void => {\n if (ts.isCallExpression(n)) {\n const nm = ts.isIdentifier(n.expression) ? n.expression.text : propAccessName(n)\n if (SUBSCRIBE_CALLS.test(nm)) subscribes = true\n if (ts.isIdentifier(n.expression) && n.expression.text === 'onCleanup') hasCleanup = true\n }\n // A `return <function>` OR `return <identifier>` is a teardown: effect() registers ANY returned\n // function value as cleanup (reactive.ts), so `const t = …; return t` / `return unsub` count.\n if (\n ts.isReturnStatement(n) &&\n n.expression &&\n (ts.isArrowFunction(n.expression) ||\n ts.isFunctionExpression(n.expression) ||\n ts.isIdentifier(n.expression))\n )\n hasCleanup = true\n ts.forEachChild(n, look)\n }\n look(fn.body)\n if (subscribes && !hasCleanup) {\n emit(\n node,\n 'MDC_PERF_005',\n 'effect() subscribes to an external source but returns no cleanup — the listener leaks ' +\n 'when the scope disposes. Return a teardown function or call onCleanup(() => …).',\n )\n }\n }\n\n const ruleAsyncEffect = (node: ts.Node): void => {\n if (!ts.isCallExpression(node) || calleeName(node) !== 'effect') return\n const fn = node.arguments[0]\n if (!fn || (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn))) return\n const isAsync = fn.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false\n if (isAsync) {\n emit(\n node,\n 'MDC_PERF_008',\n 'effect() given an async function — dependency tracking stops at the first `await` (signals ' +\n \"written afterward won't re-run the effect), and the returned Promise is ignored, not used as \" +\n 'cleanup. Keep the effect sync and launch the async work inside it: ' +\n 'effect(() => { const d = dep(); void run(d) }).',\n )\n }\n }\n\n const ruleConstFunctionStyle = (node: ts.Node): void => {\n if (!ts.isJsxAttribute(node) || !node.initializer) return\n if (!ts.isJsxExpression(node.initializer) || !node.initializer.expression) return\n const expr = node.initializer.expression\n if (!ts.isArrowFunction(expr) || ts.isBlock(expr.body)) return\n let body: ts.Expression = expr.body\n while (ts.isParenthesizedExpression(body)) body = body.expression // `() => ({...})` parens\n if (!ts.isObjectLiteralExpression(body) && !ts.isArrayLiteralExpression(body)) return\n // Only flag a PROVABLY-STATIC literal — one with NO call expression at all. A body that calls\n // anything (theme(), props.active(), select()/destructured/param accessors, helpers) may read a\n // live signal; flagging it and \"passing the literal directly\" would BREAK reactivity. Erring to\n // silence here keeps the rule from firing on the #1 styled-component pattern.\n if (!containsAnyCall(body)) {\n emit(\n node,\n 'MDC_PERF_006',\n 'A function-valued prop that returns a constant object/array (no reads) allocates a reactive ' +\n 'binding for a value that never changes. Pass the literal directly (style={{…}}).',\n )\n }\n }\n\n const ruleLargeLiteralList = (node: ts.Node): void => {\n if (ts.isJsxElement(node) || ts.isJsxFragment(node)) {\n const count = node.children.filter(\n (c) => ts.isJsxElement(c) || ts.isJsxSelfClosingElement(c) || ts.isJsxFragment(c),\n ).length\n if (count >= threshold)\n emit(\n node,\n 'MDC_PERF_007',\n `${count} static child elements rendered inline — consider List({...}) virtualization for large lists.`,\n )\n } else if (ts.isArrayLiteralExpression(node)) {\n const count = node.elements.filter(\n (c) => ts.isJsxElement(c) || ts.isJsxSelfClosingElement(c) || ts.isJsxFragment(c),\n ).length\n if (count >= threshold)\n emit(\n node,\n 'MDC_PERF_007',\n `${count} JSX elements in an array literal — consider For/List for large lists.`,\n )\n }\n }\n\n const visit = (node: ts.Node): void => {\n ruleMapJsxChild(node)\n ruleMissingKey(node)\n ruleHeavyEffect(node)\n ruleAsyncEffect(node)\n ruleRepeatedReadInLoop(node)\n ruleEffectNoCleanup(node)\n ruleConstFunctionStyle(node)\n ruleLargeLiteralList(node)\n ts.forEachChild(node, visit)\n }\n visit(sf)\n return out\n}\n"],"mappings":";;;;;;;;;;;;;;;AA2BA,MAAM,gBAAgB;CACpB;CACA;CACA;CACA;CACA;CACA;AACF;AACA,MAAM,iBAAiB,IAAI,IAAI,CAAC,cAAc,CAAC;;AAE/C,MAAM,mBAAmB;AACzB,MAAM,gBAAgB,IAAI,IAAI;CAC5B;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AACD,MAAM,kBACJ;;AAGF,SAAgB,SACd,QACA,WAAW,cACX,UAA2B,CAAC,GACd;CACd,MAAM,KAAK,GAAG,iBACZ,UACA,QACA,GAAG,aAAa,QACK,MACrB,kBAAkB,QAAQ,CAC5B;CACA,MAAM,QAAQ,IAAI,IAAI,QAAQ,cAAc,aAAa;CACzD,MAAM,YAAY,QAAQ,qBAAqB;CAC/C,MAAM,MAAoB,CAAC;CAE3B,MAAM,aAAa,SAA0B;EAC3C,MAAM,UAAU,QAAQ,QAAQ;EAChC,IAAI,SAAS,OAAO,YAAY;EAChC,OAAO,CAAC,eAAe,IAAI,IAAI;CACjC;CAGA,MAAM,4BAAY,IAAI,IAAY;CAClC,MAAM,gBAAgB,UAAqD;EACzE,IAAI,SAAS,GAAG,aAAa,MAAM,IAAI,GAAG,UAAU,IAAI,MAAM,KAAK,IAAI;CACzE;CACA,MAAM,oBAAoB,SAAwB;EAChD,IACE,GAAG,sBAAsB,IAAI,KAC7B,GAAG,aAAa,KAAK,IAAI,KACzB,KAAK,eACL,GAAG,iBAAiB,KAAK,WAAW,GACpC;GACA,MAAM,SAAS,KAAK,YAAY;GAChC,MAAM,OAAO,GAAG,aAAa,MAAM,IAC/B,OAAO,OACP,GAAG,2BAA2B,MAAM,IAClC,OAAO,KAAK,OACZ;GACN,IAAI,iBAAiB,KAAK,IAAI,GAAG,UAAU,IAAI,KAAK,KAAK,IAAI;EAC/D;EAGA,IACE,GAAG,iBAAiB,IAAI,KACxB,MAAM,IAAI,GAAG,aAAa,KAAK,UAAU,IAAI,KAAK,WAAW,OAAO,EAAE,GACtE;GACA,MAAM,MAAM,KAAK,UAAU;GAC3B,IAAI,OAAO,GAAG,0BAA0B,GAAG;SACpC,MAAM,KAAK,IAAI,YAClB,IACE,GAAG,qBAAqB,CAAC,KACzB,GAAG,aAAa,EAAE,IAAI,MACrB,EAAE,KAAK,SAAS,cAAc,EAAE,KAAK,SAAS,kBAC9C,GAAG,gBAAgB,EAAE,WAAW,KAAK,GAAG,qBAAqB,EAAE,WAAW,IAC3E;KACA,aAAa,EAAE,YAAY,WAAW,EAAE;KACxC,aAAa,EAAE,YAAY,WAAW,EAAE;IAC1C;;EAGN;EACA,GAAG,aAAa,MAAM,gBAAgB;CACxC;CACA,iBAAiB,EAAE;CAEnB,MAAM,cAAc,SAAkB;EACpC,MAAM,EAAE,MAAM,cAAc,GAAG,8BAA8B,KAAK,SAAS,EAAE,CAAC;EAC9E,OAAO;GAAE,MAAM,OAAO;GAAG,QAAQ,YAAY;EAAE;CACjD;CAIA,MAAM,cAAc,OAAO,MAAM,IAAI;CACrC,MAAM,gBAAgB,MAAe,SAA0B;EAC7D,MAAM,EAAE,SAAS,GAAG,8BAA8B,KAAK,SAAS,EAAE,CAAC;EACnE,KAAK,MAAM,MAAM,CAAC,MAAM,OAAO,CAAC,GAAG;GACjC,MAAM,IAAI,wCAAwC,KAAK,YAAY,OAAO,EAAE;GAC5E,IAAI,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,OAAO,OAAO;EAC5C;EACA,OAAO;CACT;CACA,MAAM,QAAQ,MAAe,MAAc,YAA0B;EACnE,IAAI,CAAC,UAAU,IAAI,KAAK,aAAa,MAAM,IAAI,GAAG;EAClD,IAAI,KAAK;GAAE,UAAU;GAAW;GAAM;GAAS,MAAM;GAAU,UAAU,WAAW,IAAI;EAAE,CAAC;CAC7F;CAGA,MAAM,eAAe,SAAuC;EAE1D,IAAI,GAAG,gBAAgB,IAAI,KAAK,CAAC,GAAG,QAAQ,KAAK,IAAI,GAAG,OAAO,KAAK;EACpE,OAAO;CACT;CACA,MAAM,cAAc,OAA+B;EACjD,IAAI,CAAC,GAAG,gBAAgB,EAAE,KAAK,CAAC,GAAG,qBAAqB,EAAE,GAAG,OAAO;EACpE,IAAI,QAAQ;EACZ,MAAM,QAAQ,MAAqB;GACjC,IAAI,OAAO;GACX,IAAI,GAAG,aAAa,CAAC,KAAK,GAAG,cAAc,CAAC,KAAK,GAAG,wBAAwB,CAAC,GAAG;IAC9E,QAAQ;IACR;GACF;GAEA,IACE,MAAM,OACL,GAAG,gBAAgB,CAAC,KAAK,GAAG,qBAAqB,CAAC,KAAK,GAAG,sBAAsB,CAAC,IAElF;GACF,GAAG,aAAa,GAAG,IAAI;EACzB;EACA,KAAK,GAAG,IAAI;EACZ,OAAO;CACT;CACA,MAAM,cAAc,SAClB,GAAG,aAAa,KAAK,UAAU,IAAI,KAAK,WAAW,OAAO;CAC5D,MAAM,kBAAkB,SACtB,GAAG,2BAA2B,KAAK,UAAU,IAAI,KAAK,WAAW,KAAK,OAAO;;CAE/E,MAAM,mBAAmB,SAA2B;EAClD,IAAI,QAAQ;EACZ,MAAM,QAAQ,MAAqB;GACjC,IAAI,OAAO;GACX,IAAI,GAAG,iBAAiB,CAAC,GAAG;IAC1B,QAAQ;IACR;GACF;GACA,GAAG,aAAa,GAAG,IAAI;EACzB;EACA,KAAK,IAAI;EACT,OAAO;CACT;;CAEA,MAAM,iBAAiB,SAA2B;EAChD,IAAI,QAAQ;EACZ,MAAM,QAAQ,MAAqB;GACjC,IAAI,OAAO;GACX,IACE,GAAG,iBAAiB,CAAC,KACrB,EAAE,UAAU,WAAW,KACvB,GAAG,aAAa,EAAE,UAAU,KAC5B,UAAU,IAAI,EAAE,WAAW,IAAI,GAC/B;IACA,QAAQ;IACR;GACF;GACA,GAAG,aAAa,GAAG,IAAI;EACzB;EACA,KAAK,IAAI;EACT,OAAO;CACT;;CAEA,MAAM,gBAAgB,SAA2B;EAC/C,IAAI,QAAQ;EACZ,MAAM,QAAQ,MAAqB;GACjC,IAAI,OAAO;GACX,IACE,GAAG,eAAe,CAAC,KACnB,GAAG,iBAAiB,CAAC,KACrB,GAAG,iBAAiB,CAAC,KACrB,GAAG,iBAAiB,CAAC,GACrB;IACA,QAAQ;IACR;GACF;GACA,IAAI,GAAG,iBAAiB,CAAC,KAAK,cAAc,IAAI,eAAe,CAAC,CAAC,GAAG;IAClE,QAAQ;IACR;GACF;GACA,IACE,GAAG,iBAAiB,CAAC,KACrB,GAAG,2BAA2B,EAAE,UAAU,KAC1C,GAAG,aAAa,EAAE,WAAW,UAAU,KACvC,EAAE,WAAW,WAAW,SAAS,QACjC;IACA,QAAQ;IACR;GACF;GACA,GAAG,aAAa,GAAG,IAAI;EACzB;EACA,KAAK,IAAI;EACT,OAAO;CACT;CAGA,MAAM,mBAAmB,SAAwB;EAC/C,IAAI,CAAC,GAAG,gBAAgB,IAAI,KAAK,CAAC,KAAK,YAAY;EACnD,MAAM,SAAS,KAAK;EACpB,IAAI,CAAC,UAAU,EAAE,GAAG,aAAa,MAAM,KAAK,GAAG,cAAc,MAAM,IAAI;EACvE,MAAM,OAAO,YAAY,KAAK,UAAU;EACxC,IAAI,CAAC,GAAG,iBAAiB,IAAI,KAAK,eAAe,IAAI,MAAM,OAAO;EAClE,MAAM,KAAK,KAAK,UAAU;EAC1B,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,GAAG;EAC5B,KACE,MACA,gBACA,iOAGF;CACF;CAEA,MAAM,kBAAkB,SAAwB;EAC9C,IAAI,CAAC,GAAG,iBAAiB,IAAI,GAAG;EAChC,MAAM,OAAO,WAAW,IAAI;EAC5B,IAAI,CAAC,MAAM,IAAI,IAAI,GAAG;EACtB,MAAM,MAAM,KAAK,UAAU;EAC3B,IAAI,CAAC,OAAO,CAAC,GAAG,0BAA0B,GAAG,GAAG;EAChD,IAAI,SAAS;EACb,IAAI,YAAY;EAChB,IAAI,UAAU;EACd,KAAK,MAAM,KAAK,IAAI,YAAY;GAC9B,IAAI,GAAG,mBAAmB,CAAC,GAAG,YAAY;GAC1C,KACG,GAAG,qBAAqB,CAAC,KAAK,GAAG,8BAA8B,CAAC,MACjE,EAAE,QACF,GAAG,aAAa,EAAE,IAAI,GACtB;IACA,IAAI,EAAE,KAAK,SAAS,OAAO,SAAS;IACpC,IAAI,EAAE,KAAK,SAAS,QAAQ,UAAU;GACxC;EACF;EAIA,IAAI,UAAU,aAAa,CAAC,SAAS;EACrC,KACE,MACA,gBACA,GAAG,KAAK,4MAGV;CACF;CAEA,MAAM,mBAAmB,SAAwB;EAC/C,IAAI,CAAC,GAAG,iBAAiB,IAAI,KAAK,WAAW,IAAI,MAAM,UAAU;EAEjE,IAAI,KAAK,UAAU,UAAU,GAAG;EAChC,MAAM,KAAK,KAAK,UAAU;EAC1B,IAAI,CAAC,MAAO,CAAC,GAAG,gBAAgB,EAAE,KAAK,CAAC,GAAG,qBAAqB,EAAE,GAAI;EACtE,IAAI,aAAa,GAAG,IAAI,KAAK,cAAc,GAAG,IAAI,GAChD,KACE,MACA,gBACA,sQAGF;CAEJ;CAEA,MAAM,0BAA0B,SAAwB;EACtD,IAAI;EACJ,IACE,GAAG,eAAe,IAAI,KACtB,GAAG,iBAAiB,IAAI,KACxB,GAAG,iBAAiB,IAAI,KACxB,GAAG,iBAAiB,IAAI,GAExB,OAAO,KAAK;OACP,IACL,GAAG,iBAAiB,IAAI,MACvB,eAAe,IAAI,MAAM,SAAS,eAAe,IAAI,MAAM,YAC5D;GACA,MAAM,KAAK,KAAK,UAAU;GAC1B,IAAI,OAAO,GAAG,gBAAgB,EAAE,KAAK,GAAG,qBAAqB,EAAE,IAAI,OAAO,GAAG;EAC/E;EACA,IAAI,CAAC,MAAM;EAGX,MAAM,sBAAM,IAAI,IAA+C;EAC/D,MAAM,QAAQ,MAAqB;GACjC,IACE,MAAM,SACL,GAAG,gBAAgB,CAAC,KAAK,GAAG,qBAAqB,CAAC,KAAK,GAAG,sBAAsB,CAAC,IAElF;GACF,IACE,GAAG,iBAAiB,CAAC,KACrB,EAAE,UAAU,WAAW,KACvB,GAAG,aAAa,EAAE,UAAU,KAC5B,UAAU,IAAI,EAAE,WAAW,IAAI,GAC/B;IACA,MAAM,KAAK,EAAE,WAAW;IACxB,MAAM,IAAI,IAAI,IAAI,EAAE;IACpB,IAAI,GAAG,EAAE;SACJ,IAAI,IAAI,IAAI;KAAE,OAAO;KAAG,OAAO;IAAE,CAAC;GACzC;GACA,GAAG,aAAa,GAAG,IAAI;EACzB;EACA,KAAK,IAAI;EACT,KAAK,MAAM,CAAC,IAAI,MAAM,KACpB,IAAI,EAAE,SAAS,GACb,KACE,EAAE,OACF,gBACA,KAAK,GAAG,eAAe,EAAE,MAAM,2FACQ,GAAG,IAC5C;CAGN;CAEA,MAAM,uBAAuB,SAAwB;EACnD,IAAI,CAAC,GAAG,iBAAiB,IAAI,KAAK,WAAW,IAAI,MAAM,UAAU;EACjE,MAAM,KAAK,KAAK,UAAU;EAC1B,IAAI,CAAC,MAAO,CAAC,GAAG,gBAAgB,EAAE,KAAK,CAAC,GAAG,qBAAqB,EAAE,GAAI;EACtE,IAAI,aAAa;EACjB,IAAI,aAAa;EACjB,MAAM,QAAQ,MAAqB;GACjC,IAAI,GAAG,iBAAiB,CAAC,GAAG;IAC1B,MAAM,KAAK,GAAG,aAAa,EAAE,UAAU,IAAI,EAAE,WAAW,OAAO,eAAe,CAAC;IAC/E,IAAI,gBAAgB,KAAK,EAAE,GAAG,aAAa;IAC3C,IAAI,GAAG,aAAa,EAAE,UAAU,KAAK,EAAE,WAAW,SAAS,aAAa,aAAa;GACvF;GAGA,IACE,GAAG,kBAAkB,CAAC,KACtB,EAAE,eACD,GAAG,gBAAgB,EAAE,UAAU,KAC9B,GAAG,qBAAqB,EAAE,UAAU,KACpC,GAAG,aAAa,EAAE,UAAU,IAE9B,aAAa;GACf,GAAG,aAAa,GAAG,IAAI;EACzB;EACA,KAAK,GAAG,IAAI;EACZ,IAAI,cAAc,CAAC,YACjB,KACE,MACA,gBACA,uKAEF;CAEJ;CAEA,MAAM,mBAAmB,SAAwB;EAC/C,IAAI,CAAC,GAAG,iBAAiB,IAAI,KAAK,WAAW,IAAI,MAAM,UAAU;EACjE,MAAM,KAAK,KAAK,UAAU;EAC1B,IAAI,CAAC,MAAO,CAAC,GAAG,gBAAgB,EAAE,KAAK,CAAC,GAAG,qBAAqB,EAAE,GAAI;EAEtE,IADgB,GAAG,WAAW,MAAM,MAAM,EAAE,SAAS,GAAG,WAAW,YAAY,KAAK,OAElF,KACE,MACA,gBACA,4SAIF;CAEJ;CAEA,MAAM,0BAA0B,SAAwB;EACtD,IAAI,CAAC,GAAG,eAAe,IAAI,KAAK,CAAC,KAAK,aAAa;EACnD,IAAI,CAAC,GAAG,gBAAgB,KAAK,WAAW,KAAK,CAAC,KAAK,YAAY,YAAY;EAC3E,MAAM,OAAO,KAAK,YAAY;EAC9B,IAAI,CAAC,GAAG,gBAAgB,IAAI,KAAK,GAAG,QAAQ,KAAK,IAAI,GAAG;EACxD,IAAI,OAAsB,KAAK;EAC/B,OAAO,GAAG,0BAA0B,IAAI,GAAG,OAAO,KAAK;EACvD,IAAI,CAAC,GAAG,0BAA0B,IAAI,KAAK,CAAC,GAAG,yBAAyB,IAAI,GAAG;EAK/E,IAAI,CAAC,gBAAgB,IAAI,GACvB,KACE,MACA,gBACA,8KAEF;CAEJ;CAEA,MAAM,wBAAwB,SAAwB;EACpD,IAAI,GAAG,aAAa,IAAI,KAAK,GAAG,cAAc,IAAI,GAAG;GACnD,MAAM,QAAQ,KAAK,SAAS,QACzB,MAAM,GAAG,aAAa,CAAC,KAAK,GAAG,wBAAwB,CAAC,KAAK,GAAG,cAAc,CAAC,CAClF,EAAE;GACF,IAAI,SAAS,WACX,KACE,MACA,gBACA,GAAG,MAAM,8FACX;EACJ,OAAO,IAAI,GAAG,yBAAyB,IAAI,GAAG;GAC5C,MAAM,QAAQ,KAAK,SAAS,QACzB,MAAM,GAAG,aAAa,CAAC,KAAK,GAAG,wBAAwB,CAAC,KAAK,GAAG,cAAc,CAAC,CAClF,EAAE;GACF,IAAI,SAAS,WACX,KACE,MACA,gBACA,GAAG,MAAM,uEACX;EACJ;CACF;CAEA,MAAM,SAAS,SAAwB;EACrC,gBAAgB,IAAI;EACpB,eAAe,IAAI;EACnB,gBAAgB,IAAI;EACpB,gBAAgB,IAAI;EACpB,uBAAuB,IAAI;EAC3B,oBAAoB,IAAI;EACxB,uBAAuB,IAAI;EAC3B,qBAAqB,IAAI;EACzB,GAAG,aAAa,MAAM,KAAK;CAC7B;CACA,MAAM,EAAE;CACR,OAAO;AACT"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mindees/compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.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.
|
|
27
|
+
"@mindees/core": "0.20.0"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "tsdown",
|