@trackunit/iris-app 2.5.12 → 2.5.14
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/CHANGELOG.md +17 -0
- package/migrations/entry.js.map +1 -0
- package/migrations/p-content-space-codemod/invent-content-space.js +277 -0
- package/migrations/p-content-space-codemod/invent-content-space.js.map +1 -0
- package/migrations/p-content-space-codemod/orchestrator.js +140 -0
- package/migrations/p-content-space-codemod/orchestrator.js.map +1 -0
- package/migrations/p-content-space-codemod/remove-redundant-padding.js +37 -0
- package/migrations/p-content-space-codemod/remove-redundant-padding.js.map +1 -0
- package/migrations/p-content-space-codemod/report-run-messaging.js +73 -0
- package/migrations/p-content-space-codemod/report-run-messaging.js.map +1 -0
- package/migrations/p-content-space-codemod/report-unresolved.js +87 -0
- package/migrations/p-content-space-codemod/report-unresolved.js.map +1 -0
- package/migrations/p-content-space-codemod/resolve-content-shell.js +968 -0
- package/migrations/p-content-space-codemod/resolve-content-shell.js.map +1 -0
- package/migrations/p-content-space-codemod/rewrite-page-content.js +42 -0
- package/migrations/p-content-space-codemod/rewrite-page-content.js.map +1 -0
- package/migrations/p-content-space-codemod/shell-skip-reason.js +41 -0
- package/migrations/p-content-space-codemod/shell-skip-reason.js.map +1 -0
- package/migrations/utils/classname-ast-rewrite.js +220 -0
- package/migrations/utils/classname-ast-rewrite.js.map +1 -0
- package/migrations/utils/resolve-extension-types.js +208 -0
- package/migrations/utils/resolve-extension-types.js.map +1 -0
- package/migrations.json +7 -1
- package/package.json +8 -6
- package/src/executors/submit/executor.js.map +1 -0
- package/src/executors/unpublish/executor.js.map +1 -0
- package/src/executors/utils/authentication.js.map +1 -0
- package/src/executors/utils/irisAppServerSettings.js.map +1 -0
- package/src/executors/utils/src/index.js.map +1 -0
- package/src/generators/ai-agent-sync/generator.js.map +1 -0
- package/src/generators/create/generator.js.map +1 -0
- package/src/generators/extend/dependencies.js.map +1 -0
- package/src/generators/extend/generator.js.map +1 -0
- package/src/generators/preset/generator.js.map +1 -0
- package/src/generators/utils/get-package-manifest.js.map +1 -0
- package/src/generators/utils/link-claude-skills.js.map +1 -0
- package/src/index.js.map +1 -0
- package/src/utils/ast/astUtils.js.map +1 -0
- package/src/utils/fileUpdater.js.map +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-run-messaging.js","sourceRoot":"","sources":["../../../../../../libs/iris-app-sdk/iris-app/migrations/p-content-space-codemod/report-run-messaging.ts"],"names":[],"mappings":";;;AAAA,wEAA8D;AAC9D,2DAAkH;AAElH,MAAM,cAAc,GAAG,yBAAyB,CAAC;AASjD;;;;;GAKG;AACI,MAAM,gCAAgC,GAAG,GAAW,EAAE,CAC3D,KAAK,cAAc,8GAA8G,CAAC;AADvH,QAAA,gCAAgC,oCACuF;AAEpI;;;;GAIG;AACI,MAAM,6BAA6B,GAAG,GAAW,EAAE,CACxD,KAAK,cAAc,qYAAqY,qCAAiB,EAAE,CAAC;AADja,QAAA,6BAA6B,iCACoY;AAE9a;;;GAGG;AACI,MAAM,sBAAsB,GAAG,CAAC,MAA+B,EAAU,EAAE;IAChF,MAAM,YAAY,GAChB,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,MAAM,CAAC,UAAU,CAAC,IAAI,MAAM,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;IAE/G,IAAI,MAAM,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,KAAK,cAAc,gBAAgB,MAAM,CAAC,WAAW,MAAM,MAAM,CAAC,UAAU,oBAAoB,MAAM,CAAC,gBAAgB,IAAI,YAAY,IAAI,uDAAuD,SAAS,MAAM,CAAC,QAAQ,EAAE,CAAC;IACtO,CAAC;IAED,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACpC,OAAO,KAAK,cAAc,gBAAgB,MAAM,CAAC,WAAW,MAAM,MAAM,CAAC,UAAU,sBAAsB,YAAY,mFAAmF,MAAM,CAAC,QAAQ,EAAE,CAAC;IAC5N,CAAC;IAED,OAAO,KAAK,cAAc,gBAAgB,MAAM,CAAC,WAAW,MAAM,MAAM,CAAC,UAAU,gOAAgO,MAAM,CAAC,QAAQ,EAAE,CAAC;AACvU,CAAC,CAAC;AAbW,QAAA,sBAAsB,0BAajC;AAEF;;;;GAIG;AACI,MAAM,8BAA8B,GAAG,CAAC,OAA6C,EAAiB,EAAE;IAC7G,MAAM,QAAQ,GAAkB,EAAE,CAAC;IACnC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACpF,SAAS;QACX,CAAC;QACD,QAAQ,CAAC,IAAI,CACX,KAAK,cAAc,wBAAwB,MAAM,CAAC,WAAW,MAAM,MAAM,CAAC,QAAQ,0GAA0G,CAC7L,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAXW,QAAA,8BAA8B,kCAWzC;AAEF;;;;GAIG;AACI,MAAM,0BAA0B,GAAG,CAAC,OAA2B,EAAiB,EAAE;IACvF,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;QAC7B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,QAAQ,GAAG,IAAA,sCAA8B,EAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACjE,IAAI,OAAO,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzC,QAAQ,CAAC,IAAI,CAAC,IAAA,qCAA6B,GAAE,CAAC,CAAC;IACjD,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC,IAAA,wCAAgC,GAAE,CAAC,CAAC;IAClD,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAXW,QAAA,0BAA0B,8BAWrC;AAEF,MAAM,sBAAsB,GAAG,CAAC,cAAsB,EAAW,EAAE;IACjE,OAAO,IAAA,kCAAY,EAAC,cAAc,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC9E,CAAC,CAAC","sourcesContent":["import { splitClasses } from \"@trackunit/css-classname-utils\";\nimport { PADDING_DOCS_LINK, type MigrationChangeReport, type UnresolvedSurfaceReport } from \"./report-unresolved\";\n\nconst MIGRATION_NAME = \"p-content-space-codemod\";\n\nexport type AsaEndOfRunContext = {\n /** True when any ASA extension was visited this migration run. */\n readonly asaWorkOccurred: boolean;\n readonly changes: ReadonlyArray<MigrationChangeReport>;\n readonly unresolvedReports: ReadonlyArray<UnresolvedSurfaceReport>;\n};\n\n/**\n * Developer-facing end-of-run reminder: verify appearance after migration, and\n * note that intentional edge-to-edge (no content inset) remains an allowed choice.\n * Emitted at most once per run when any ASA work happened — including runs that\n * only produced unresolved reports and no invent/convert edits.\n */\nexport const buildVerifyAndEdgeToEdgeReminder = (): string =>\n ` ${MIGRATION_NAME}: verify UI after migration; edge-to-edge content (no p-content-space) is an allowed choice when intentional`;\n\n/**\n * Explains what an inventable overflow content shell is when the migration\n * could not auto-pad one or more Asset/Site/Admin surfaces. Emitted once per\n * run that produced unresolved reports.\n */\nexport const buildUnresolvedInventGuidance = (): string =>\n ` ${MIGRATION_NAME}: could not auto-invent p-content-space on the surface(s) above — invent only targets a single inventable overflow content shell (a div/main/section with an editable className that itself owns overflow-auto/scroll, with no PageHeader/Topbar child). Card-only roots, multi-route graphs, and unclear shells need a manual p-content-space on that scroll owner (or accept edge-to-edge). See ${PADDING_DOCS_LINK}`;\n\n/**\n * Per-extension unresolved warning. Prefer this over a bare \"no token\" line so\n * developers learn *why* invent skipped them.\n */\nexport const formatUnresolvedReport = (report: UnresolvedSurfaceReport): string => {\n const reasonSuffix =\n report.skipReason === undefined ? \"\" : ` — reason [${report.skipReason.code}]: ${report.skipReason.message}`;\n\n if (report.skippedRouteName !== undefined) {\n return ` ${MIGRATION_NAME}: extension \"${report.extensionId}\" (${report.sourceRoot}) skipped route \"${report.skippedRouteName}\"${reasonSuffix || \" — no inventable overflow content shell on that route\"}; see ${report.docsLink}`;\n }\n\n if (report.skipReason !== undefined) {\n return ` ${MIGRATION_NAME}: extension \"${report.extensionId}\" (${report.sourceRoot}) not auto-invented${reasonSuffix}; add p-content-space manually on the scroll owner or accept edge-to-edge — see ${report.docsLink}`;\n }\n\n return ` ${MIGRATION_NAME}: extension \"${report.extensionId}\" (${report.sourceRoot}) has no page-content/p-content-space and no inventable overflow content shell (needs a scrolling div/main/section with editable className, not e.g. a bare Card) — add p-content-space manually or accept edge-to-edge; see ${report.docsLink}`;\n};\n\n/**\n * Advisory-only scrollbar notes for invent/convert onto a self-scrolling\n * (`overflow-*`) shell. Cannot measure real DOM scrollbars in the codemod —\n * documents the preferred flush-scrollbar outcome after content owns padding.\n */\nexport const buildScrollbarAdvisoryMessages = (changes: ReadonlyArray<MigrationChangeReport>): Array<string> => {\n const messages: Array<string> = [];\n for (const change of changes) {\n if (!shellHasOverflowScroll(change.before) && !shellHasOverflowScroll(change.after)) {\n continue;\n }\n messages.push(\n ` ${MIGRATION_NAME}: scrollbar note on \"${change.extensionId}\" (${change.filePath}): shell uses overflow scrolling — prefer a flush scrollbar at the panel edge after content owns padding`\n );\n }\n return messages;\n};\n\n/**\n * Collects end-of-run ASA developer messages (scrollbar advisories, unresolved\n * invent guidance when needed, + single verify/edge-to-edge reminder). Empty\n * when no ASA work occurred this run.\n */\nexport const collectAsaEndOfRunMessages = (context: AsaEndOfRunContext): Array<string> => {\n if (!context.asaWorkOccurred) {\n return [];\n }\n\n const messages = buildScrollbarAdvisoryMessages(context.changes);\n if (context.unresolvedReports.length > 0) {\n messages.push(buildUnresolvedInventGuidance());\n }\n messages.push(buildVerifyAndEdgeToEdgeReminder());\n return messages;\n};\n\nconst shellHasOverflowScroll = (classnameValue: string): boolean => {\n return splitClasses(classnameValue).some(cls => /(^|:)overflow-/.test(cls));\n};\n"]}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PADDING_DOCS_LINK = void 0;
|
|
4
|
+
exports.findUnresolvedSurface = findUnresolvedSurface;
|
|
5
|
+
const css_classname_utils_1 = require("@trackunit/css-classname-utils");
|
|
6
|
+
const classname_ast_rewrite_1 = require("../utils/classname-ast-rewrite");
|
|
7
|
+
const resolve_content_shell_1 = require("./resolve-content-shell");
|
|
8
|
+
const PAGE_CONTENT_TOKEN = "page-content";
|
|
9
|
+
const CONTENT_SPACE_TOKEN = "p-content-space";
|
|
10
|
+
const RESOLVED_TOKENS = [PAGE_CONTENT_TOKEN, CONTENT_SPACE_TOKEN];
|
|
11
|
+
const tokenBase = (token) => (token.includes(":") ? token.slice(token.lastIndexOf(":") + 1) : token);
|
|
12
|
+
const isResolvedToken = (token) => RESOLVED_TOKENS.includes(tokenBase(token));
|
|
13
|
+
const DEFAULT_ENTRY_COMPONENT_FILE = "index.tsx";
|
|
14
|
+
/** In-repo docs for inventable overflow content shells (GLU-1571 / p-content-space migration). */
|
|
15
|
+
exports.PADDING_DOCS_LINK = "docs/tech/TAILWIND-ARCHITECTURE.md#p-content-space-inventable-shell";
|
|
16
|
+
/**
|
|
17
|
+
* Read-only check for whether a single file already contains `page-content`
|
|
18
|
+
* or `p-content-space` as a whole classname token (not matching inside e.g.
|
|
19
|
+
* `page-content-with-gap`). Used as a suppressor on unresolvable graphs so
|
|
20
|
+
* resolver limits do not permanently light up already-migrated extensions.
|
|
21
|
+
*/
|
|
22
|
+
function fileHasResolvedToken(content) {
|
|
23
|
+
if (!RESOLVED_TOKENS.some(token => content.includes(token)))
|
|
24
|
+
return false;
|
|
25
|
+
const program = (0, classname_ast_rewrite_1.parseTsestreeSource)(content);
|
|
26
|
+
const locations = (0, classname_ast_rewrite_1.findClassnameLocations)(program);
|
|
27
|
+
return locations.some(location => (0, css_classname_utils_1.splitClasses)(location.value).some(isResolvedToken));
|
|
28
|
+
}
|
|
29
|
+
function sourceRootHasResolvedToken(tree, extension) {
|
|
30
|
+
let found = false;
|
|
31
|
+
(0, classname_ast_rewrite_1.visitRewritableFiles)(tree, extension.sourceRoot, (_filePath, content) => {
|
|
32
|
+
if (found)
|
|
33
|
+
return;
|
|
34
|
+
if (fileHasResolvedToken(content)) {
|
|
35
|
+
found = true;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return found;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Determines whether the inventable content shell of an Asset Home/Site Home/Admin
|
|
42
|
+
* extension still lacks `page-content` / `p-content-space`, and if so, builds a
|
|
43
|
+
* report entry describing it.
|
|
44
|
+
*
|
|
45
|
+
* A token on a nested file is not enough when the graph *resolves*: Device
|
|
46
|
+
* Administration hid behind `p-content-space` on a job-details leaf while the
|
|
47
|
+
* layout shell still used `pb-responsive-space` (GLU-1903). Returns `null` only
|
|
48
|
+
* when every *resolved content shell* already carries a resolved token (or still
|
|
49
|
+
* uses `page-content`, which `rewritePageContent` handles separately so it must
|
|
50
|
+
* not be double-reported).
|
|
51
|
+
*
|
|
52
|
+
* An unresolvable graph is *not* treated as a leftover if any rewritable file
|
|
53
|
+
* already has a resolved token — otherwise Device Administration (and ~24 other
|
|
54
|
+
* ASA extensions the walker cannot follow) stay permanently-on after padding.
|
|
55
|
+
* Unresolvable graphs with no token anywhere still report.
|
|
56
|
+
*
|
|
57
|
+
* This function is read-only: it never calls `tree.write`. Callers must run it
|
|
58
|
+
* against Tree state that already reflects `rewritePageContent`'s and
|
|
59
|
+
* `removeRedundantPadding`'s edits for the same extension, since those two
|
|
60
|
+
* transforms mutate `page-content` into `p-content-space` in place — running
|
|
61
|
+
* this check first, against pre-rewrite content, would misreport a
|
|
62
|
+
* just-fixed extension as unresolved.
|
|
63
|
+
*
|
|
64
|
+
* A `sourceRoot` that doesn't exist in the Tree at all (or contains no
|
|
65
|
+
* rewritable files) is treated the same as "no page-content and no
|
|
66
|
+
* p-content-space found": it gets reported, not silently skipped.
|
|
67
|
+
*/
|
|
68
|
+
function findUnresolvedSurface(tree, extension) {
|
|
69
|
+
const resolved = (0, resolve_content_shell_1.resolveContentShells)(tree, extension);
|
|
70
|
+
if (resolved.status === "resolved") {
|
|
71
|
+
const everyShellHasResolvedToken = resolved.shells.every(shell => (0, css_classname_utils_1.splitClasses)(shell.combinedClassnameValue).some(isResolvedToken));
|
|
72
|
+
if (everyShellHasResolvedToken) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else if (sourceRootHasResolvedToken(tree, extension)) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const entryComponentPath = `${extension.sourceRoot}/${extension.main ?? DEFAULT_ENTRY_COMPONENT_FILE}`;
|
|
80
|
+
return {
|
|
81
|
+
extensionId: extension.id,
|
|
82
|
+
sourceRoot: extension.sourceRoot,
|
|
83
|
+
entryComponentPath: tree.exists(entryComponentPath) ? entryComponentPath : null,
|
|
84
|
+
docsLink: exports.PADDING_DOCS_LINK,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=report-unresolved.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-unresolved.js","sourceRoot":"","sources":["../../../../../../libs/iris-app-sdk/iris-app/migrations/p-content-space-codemod/report-unresolved.ts"],"names":[],"mappings":";;;AAyGA,sDAqBC;AA7HD,wEAA8D;AAC9D,0EAAmH;AAEnH,mEAA+D;AAE/D,MAAM,kBAAkB,GAAG,cAAc,CAAC;AAC1C,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;AAC9C,MAAM,eAAe,GAAkB,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,CAAC;AAEjF,MAAM,SAAS,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AAErH,MAAM,eAAe,GAAG,CAAC,KAAa,EAAW,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AAE/F,MAAM,4BAA4B,GAAG,WAAW,CAAC;AAEjD,kGAAkG;AACrF,QAAA,iBAAiB,GAAG,qEAAqE,CAAC;AAmCvG;;;;;GAKG;AACH,SAAS,oBAAoB,CAAC,OAAe;IAC3C,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAE1E,MAAM,OAAO,GAAG,IAAA,2CAAmB,EAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,IAAA,8CAAsB,EAAC,OAAO,CAAC,CAAC;IAClD,OAAO,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAA,kCAAY,EAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;AACxF,CAAC;AAED,SAAS,0BAA0B,CAAC,IAAU,EAAE,SAA4B;IAC1E,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,IAAA,4CAAoB,EAAC,IAAI,EAAE,SAAS,CAAC,UAAU,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE;QACtE,IAAI,KAAK;YAAE,OAAO;QAClB,IAAI,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC;YAClC,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,SAAgB,qBAAqB,CAAC,IAAU,EAAE,SAA4B;IAC5E,MAAM,QAAQ,GAAG,IAAA,4CAAoB,EAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACvD,IAAI,QAAQ,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QACnC,MAAM,0BAA0B,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAC/D,IAAA,kCAAY,EAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CACjE,CAAC;QACF,IAAI,0BAA0B,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;SAAM,IAAI,0BAA0B,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC;QACvD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,kBAAkB,GAAG,GAAG,SAAS,CAAC,UAAU,IAAI,SAAS,CAAC,IAAI,IAAI,4BAA4B,EAAE,CAAC;IAEvG,OAAO;QACL,WAAW,EAAE,SAAS,CAAC,EAAE;QACzB,UAAU,EAAE,SAAS,CAAC,UAAU;QAChC,kBAAkB,EAAE,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI;QAC/E,QAAQ,EAAE,yBAAiB;KAC5B,CAAC;AACJ,CAAC","sourcesContent":["import type { Tree } from \"@nx/devkit\";\nimport { splitClasses } from \"@trackunit/css-classname-utils\";\nimport { findClassnameLocations, parseTsestreeSource, visitRewritableFiles } from \"../utils/classname-ast-rewrite\";\nimport type { ResolvedExtension } from \"../utils/resolve-extension-types\";\nimport { resolveContentShells } from \"./resolve-content-shell\";\n\nconst PAGE_CONTENT_TOKEN = \"page-content\";\nconst CONTENT_SPACE_TOKEN = \"p-content-space\";\nconst RESOLVED_TOKENS: Array<string> = [PAGE_CONTENT_TOKEN, CONTENT_SPACE_TOKEN];\n\nconst tokenBase = (token: string): string => (token.includes(\":\") ? token.slice(token.lastIndexOf(\":\") + 1) : token);\n\nconst isResolvedToken = (token: string): boolean => RESOLVED_TOKENS.includes(tokenBase(token));\n\nconst DEFAULT_ENTRY_COMPONENT_FILE = \"index.tsx\";\n\n/** In-repo docs for inventable overflow content shells (GLU-1571 / p-content-space migration). */\nexport const PADDING_DOCS_LINK = \"docs/tech/TAILWIND-ARCHITECTURE.md#p-content-space-inventable-shell\";\n\nexport type MigrationChangeKind = \"addition\" | \"conversion\";\n\n/**\n * Structured invent/convert outcome for a single content-shell edit. Emitted\n * so the orchestrator can log exactly what changed (added vs converted).\n */\nexport type MigrationChangeReport = {\n extensionId: string;\n sourceRoot: string;\n filePath: string;\n kind: MigrationChangeKind;\n /** Classname string on the shell before invent/convert. */\n before: string;\n /** Classname string on the shell after invent/convert. */\n after: string;\n /** Custom shell padding tokens removed (empty for pure additions). */\n removedTokens: Array<string>;\n};\n\nimport type { ShellSkipReason } from \"./shell-skip-reason\";\n\nexport type UnresolvedSurfaceReport = {\n extensionId: string;\n sourceRoot: string;\n /** Best-effort resolved path to the extension's entry component; `null` when it can't be found in the Tree. */\n entryComponentPath: string | null;\n docsLink: string;\n /** Route component or imported route binding that could not be analysed on a partial graph. */\n skippedRouteName?: string;\n /** Why invent/resolve declined this surface (when known). */\n skipReason?: ShellSkipReason;\n};\n\n/**\n * Read-only check for whether a single file already contains `page-content`\n * or `p-content-space` as a whole classname token (not matching inside e.g.\n * `page-content-with-gap`). Used as a suppressor on unresolvable graphs so\n * resolver limits do not permanently light up already-migrated extensions.\n */\nfunction fileHasResolvedToken(content: string): boolean {\n if (!RESOLVED_TOKENS.some(token => content.includes(token))) return false;\n\n const program = parseTsestreeSource(content);\n const locations = findClassnameLocations(program);\n return locations.some(location => splitClasses(location.value).some(isResolvedToken));\n}\n\nfunction sourceRootHasResolvedToken(tree: Tree, extension: ResolvedExtension): boolean {\n let found = false;\n visitRewritableFiles(tree, extension.sourceRoot, (_filePath, content) => {\n if (found) return;\n if (fileHasResolvedToken(content)) {\n found = true;\n }\n });\n return found;\n}\n\n/**\n * Determines whether the inventable content shell of an Asset Home/Site Home/Admin\n * extension still lacks `page-content` / `p-content-space`, and if so, builds a\n * report entry describing it.\n *\n * A token on a nested file is not enough when the graph *resolves*: Device\n * Administration hid behind `p-content-space` on a job-details leaf while the\n * layout shell still used `pb-responsive-space` (GLU-1903). Returns `null` only\n * when every *resolved content shell* already carries a resolved token (or still\n * uses `page-content`, which `rewritePageContent` handles separately so it must\n * not be double-reported).\n *\n * An unresolvable graph is *not* treated as a leftover if any rewritable file\n * already has a resolved token — otherwise Device Administration (and ~24 other\n * ASA extensions the walker cannot follow) stay permanently-on after padding.\n * Unresolvable graphs with no token anywhere still report.\n *\n * This function is read-only: it never calls `tree.write`. Callers must run it\n * against Tree state that already reflects `rewritePageContent`'s and\n * `removeRedundantPadding`'s edits for the same extension, since those two\n * transforms mutate `page-content` into `p-content-space` in place — running\n * this check first, against pre-rewrite content, would misreport a\n * just-fixed extension as unresolved.\n *\n * A `sourceRoot` that doesn't exist in the Tree at all (or contains no\n * rewritable files) is treated the same as \"no page-content and no\n * p-content-space found\": it gets reported, not silently skipped.\n */\nexport function findUnresolvedSurface(tree: Tree, extension: ResolvedExtension): UnresolvedSurfaceReport | null {\n const resolved = resolveContentShells(tree, extension);\n if (resolved.status === \"resolved\") {\n const everyShellHasResolvedToken = resolved.shells.every(shell =>\n splitClasses(shell.combinedClassnameValue).some(isResolvedToken)\n );\n if (everyShellHasResolvedToken) {\n return null;\n }\n } else if (sourceRootHasResolvedToken(tree, extension)) {\n return null;\n }\n\n const entryComponentPath = `${extension.sourceRoot}/${extension.main ?? DEFAULT_ENTRY_COMPONENT_FILE}`;\n\n return {\n extensionId: extension.id,\n sourceRoot: extension.sourceRoot,\n entryComponentPath: tree.exists(entryComponentPath) ? entryComponentPath : null,\n docsLink: PADDING_DOCS_LINK,\n };\n}\n"]}
|