@aceshooting/lyra-ui 21.0.0 → 21.1.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/CHANGELOG.md +56 -0
- package/README.md +6 -3
- package/custom-elements.json +1 -1
- package/dist/ai/runtime.js +1 -1
- package/dist/ai/types.d.ts +13 -5
- package/dist/cli/lyra-rename-ledger.mjs +1 -0
- package/dist/cli/migrate-wa.mjs +33 -13
- package/dist/cli/migration-contract.json +1 -1
- package/dist/components/agent-tools/agent-trace/agent-trace.class.d.ts +5 -3
- package/dist/components/agent-tools/agent-trace/agent-trace.class.js +3 -2
- package/dist/components/agent-tools/tool-call-block/tool-call-block.class.d.ts +15 -3
- package/dist/components/agent-tools/tool-call-block/tool-call-block.class.js +3 -3
- package/dist/components/agent-tools/tool-call-chip/tool-call-chip.class.d.ts +24 -8
- package/dist/components/agent-tools/tool-call-chip/tool-call-chip.class.js +3 -3
- package/dist/components/agent-tools/tool-param-form/tool-param-form.class.js +4 -0
- package/dist/components/agent-tools/tool-result-dialog/tool-result-dialog.class.d.ts +9 -5
- package/dist/components/agent-tools/tool-result-dialog/tool-result-dialog.class.js +2 -2
- package/dist/components/agent-tools/tool-result-dialog/tool-result-dialog.styles.js +1 -1
- package/dist/components/agent-tools/tool-status.js +2 -2
- package/dist/components/agent-tools/tool-timeline/tool-timeline.class.d.ts +8 -0
- package/dist/components/agent-tools/tool-timeline/tool-timeline.class.js +3 -2
- package/dist/components/agent-tools/tool-timeline/tool-timeline.styles.js +1 -1
- package/dist/components/agent-tools/trace-tree/span.d.ts +2 -2
- package/dist/components/charts/chart/box-plot.class.d.ts +3 -1
- package/dist/components/charts/chart/box-plot.class.js +9 -9
- package/dist/components/charts/chart/chart-bidi.d.ts +109 -0
- package/dist/components/charts/chart/chart-bidi.js +1 -0
- package/dist/components/charts/chart/chart.class.d.ts +17 -2
- package/dist/components/charts/chart/chart.class.js +10 -8
- package/dist/components/charts/chart/histogram.class.js +1 -1
- package/dist/components/charts/chart/lite-chart.class.d.ts +24 -5
- package/dist/components/charts/chart/lite-chart.class.js +9 -9
- package/dist/components/charts/chart/lite-chart.styles.js +1 -1
- package/dist/components/conversation/code-block/shiki-dark-theme.d.ts +2 -2
- package/dist/components/conversation/code-block/shiki-dark-theme.js +1 -1
- package/dist/components/conversation/conversation-item/conversation-item.class.d.ts +18 -2
- package/dist/components/conversation/conversation-item/conversation-item.class.js +3 -3
- package/dist/components/conversation/conversation-item/conversation-item.styles.js +1 -1
- package/dist/components/conversation/markdown/markdown-base.class.js +1 -1
- package/dist/components/conversation/markdown/markdown-core.class.d.ts +3 -1
- package/dist/components/conversation/markdown/markdown.class.d.ts +3 -1
- package/dist/components/conversation/message-actions/message-actions.class.d.ts +7 -2
- package/dist/components/conversation/message-actions/message-actions.class.js +2 -2
- package/dist/components/conversation/message-parts/message-parts.class.d.ts +6 -2
- package/dist/components/conversation/message-parts/message-parts.class.js +5 -3
- package/dist/components/conversation/selection-toolbar/selection-toolbar.class.d.ts +3 -2
- package/dist/components/conversation/selection-toolbar/selection-toolbar.class.js +1 -1
- package/dist/components/conversation/streaming-text/streaming-text-base.class.d.ts +1 -5
- package/dist/components/conversation/streaming-text/streaming-text-base.class.js +2 -2
- package/dist/components/conversation/streaming-text/streaming-text-core.class.d.ts +7 -1
- package/dist/components/conversation/streaming-text/streaming-text-core.class.js +3 -4
- package/dist/components/conversation/streaming-text/streaming-text.class.d.ts +7 -1
- package/dist/components/conversation/streaming-text/streaming-text.class.js +3 -4
- package/dist/components/conversation/thread-list/thread-list.class.d.ts +13 -2
- package/dist/components/conversation/thread-list/thread-list.class.js +3 -3
- package/dist/components/data/data-grid/data-grid.class.js +1 -1
- package/dist/components/data/document-library/document-library.class.js +2 -0
- package/dist/components/data/flow-canvas/flow-canvas.class.js +1 -1
- package/dist/components/data/flow-minimap/flow-minimap.class.js +1 -1
- package/dist/components/data/flow-node/flow-node.class.d.ts +6 -2
- package/dist/components/data/flow-node/flow-node.class.js +6 -2
- package/dist/components/data/flow-node/flow-node.styles.js +1 -1
- package/dist/components/data/gauge/gauge.styles.js +1 -1
- package/dist/components/data/heatmap/heatmap.class.d.ts +3 -0
- package/dist/components/data/heatmap/heatmap.class.js +2 -2
- package/dist/components/data/heatmap/heatmap.styles.js +1 -1
- package/dist/components/data/sparkline/sparkline.class.d.ts +5 -3
- package/dist/components/data/stat/stat.class.d.ts +4 -3
- package/dist/components/data/table/table.class.d.ts +32 -5
- package/dist/components/data/table/table.class.js +3 -2
- package/dist/components/forms/button/button.class.d.ts +7 -1
- package/dist/components/forms/button/button.styles.js +1 -1
- package/dist/components/forms/date-picker/date-input.styles.js +1 -1
- package/dist/components/forms/input/input.styles.js +1 -1
- package/dist/components/forms/rubric-form/rubric-form.class.js +1 -0
- package/dist/components/layout/app-rail/app-rail.class.d.ts +50 -11
- package/dist/components/layout/app-rail/app-rail.class.js +2 -2
- package/dist/components/layout/command-palette/command-palette.class.js +1 -1
- package/dist/components/layout/menu/menu.class.d.ts +6 -5
- package/dist/components/layout/split-panel/split-panel.class.d.ts +2 -1
- package/dist/components/media/flag/language-map.d.ts +8 -0
- package/dist/components/media/flag/language-map.js +1 -1
- package/dist/components/media/map/map-loader.d.ts +7 -1
- package/dist/components/media/map/map.class.d.ts +111 -5
- package/dist/components/media/map/map.class.js +3 -3
- package/dist/components/media/media-card/media-card.class.d.ts +18 -5
- package/dist/components/media/media-card/media-card.class.js +1 -1
- package/dist/components/overlays/overlay/popover.class.d.ts +20 -0
- package/dist/components/overlays/overlay/popover.class.js +2 -2
- package/dist/components/retrieval/graph/graph.class.d.ts +3 -3
- package/dist/components/retrieval/graph/graph.class.js +1 -1
- package/dist/components/retrieval/knowledge-graph-explorer/knowledge-graph-explorer.class.d.ts +5 -3
- package/dist/components/retrieval/knowledge-graph-explorer/knowledge-graph-explorer.class.js +3 -2
- package/dist/components/retrieval/mind-map/mind-map.class.d.ts +3 -3
- package/dist/components/retrieval/mind-map/mind-map.class.js +1 -1
- package/dist/components/retrieval/retrieval-results/retrieval-results.class.d.ts +4 -1
- package/dist/components/retrieval/retrieval-results/retrieval-results.class.js +5 -1
- package/dist/components/retrieval/source-picker/source-picker.class.d.ts +4 -1
- package/dist/components/retrieval/source-picker/source-picker.class.js +5 -1
- package/dist/components/utility/icon/icon.class.d.ts +9 -3
- package/dist/components/utility/icon/icon.class.js +1 -1
- package/dist/components/utility/mutation-observer/mutation-observer.class.d.ts +14 -8
- package/dist/components/utility/mutation-observer/mutation-observer.class.js +1 -1
- package/dist/components/viewers/document-viewer/document-viewer.class.d.ts +6 -1
- package/dist/components/viewers/document-viewer/document-viewer.class.js +7 -1
- package/dist/components/viewers/document-viewer/registry.d.ts +12 -3
- package/dist/components/viewers/geojson-view/geojson-view.class.d.ts +13 -5
- package/dist/components/viewers/geojson-view/geojson-view.class.js +1 -1
- package/dist/components/viewers/geojson-view/geojson-viewer.class.d.ts +3 -2
- package/dist/custom-elements-jsx.d.ts +1 -1
- package/dist/events.d.ts +644 -1242
- package/dist/internal/anchored-overlay-runtime.d.ts +2 -2
- package/dist/internal/cell-range.d.ts +0 -6
- package/dist/internal/cell-range.js +1 -1
- package/dist/internal/css-token-length.d.ts +7 -0
- package/dist/internal/css-token-length.js +1 -0
- package/dist/internal/default-strings.generated.d.ts +1 -1
- package/dist/internal/default-strings.generated.js +1 -1
- package/dist/internal/deferred-focus-return.d.ts +7 -4
- package/dist/internal/deferred-focus-return.js +1 -1
- package/dist/internal/dev-mode-attribute-warning.d.ts +35 -0
- package/dist/internal/dev-mode-attribute-warning.js +1 -1
- package/dist/internal/localization-runtime.js +1 -1
- package/dist/internal/localization-types.d.ts +1 -1
- package/dist/internal/localization.js +2 -2
- package/dist/internal/package-metadata.d.ts +1 -1
- package/dist/internal/package-metadata.js +1 -1
- package/dist/internal/papaparse-loader.d.ts +1 -1
- package/dist/internal/papaparse-loader.js +1 -1
- package/dist/internal/positioner.d.ts +10 -0
- package/dist/internal/positioner.js +1 -1
- package/dist/internal/shared-unions.d.ts +2 -1
- package/dist/internal/slotted-overlay-controller.d.ts +20 -0
- package/dist/internal/slotted-overlay-controller.js +1 -0
- package/dist/internal/text-quote.d.ts +1 -1
- package/dist/internal/text-quote.js +1 -1
- package/dist/internal/theme-observation.d.ts +4 -0
- package/dist/internal/theme-observation.js +1 -0
- package/dist/internal/theme-watcher.js +1 -1
- package/dist/localization.d.ts +6 -1
- package/dist/localization.js +1 -1
- package/dist/lyra.d.ts +1 -1
- package/dist/lyra.js +1 -1
- package/dist/svelte.d.ts +1 -1
- package/dist/testing/lyra-tag-event-map.js +1 -1
- package/dist/translations/ar/agent-tools.js +1 -1
- package/dist/translations/cs/agent-tools.js +1 -1
- package/dist/translations/da/agent-tools.js +1 -1
- package/dist/translations/de/agent-tools.js +1 -1
- package/dist/translations/de-CH/agent-tools.js +1 -1
- package/dist/translations/es/agent-tools.js +1 -1
- package/dist/translations/fa/agent-tools.js +1 -1
- package/dist/translations/fi/agent-tools.js +1 -1
- package/dist/translations/fr/agent-tools.js +1 -1
- package/dist/translations/he/agent-tools.js +1 -1
- package/dist/translations/hi/agent-tools.js +1 -1
- package/dist/translations/hr/agent-tools.js +1 -1
- package/dist/translations/hu/agent-tools.js +1 -1
- package/dist/translations/id/agent-tools.js +1 -1
- package/dist/translations/it/agent-tools.js +1 -1
- package/dist/translations/ja/agent-tools.js +1 -1
- package/dist/translations/kk/agent-tools.js +1 -1
- package/dist/translations/ko/agent-tools.js +1 -1
- package/dist/translations/nb/agent-tools.js +1 -1
- package/dist/translations/nl/agent-tools.js +1 -1
- package/dist/translations/nn/agent-tools.js +1 -1
- package/dist/translations/pl/agent-tools.js +1 -1
- package/dist/translations/pt-BR/agent-tools.js +1 -1
- package/dist/translations/pt-PT/agent-tools.js +1 -1
- package/dist/translations/pt-PT/charts.js +1 -1
- package/dist/translations/pt-PT/conversation.js +1 -1
- package/dist/translations/pt-PT/data.js +1 -1
- package/dist/translations/pt-PT/forms.js +1 -1
- package/dist/translations/pt-PT/layout.js +1 -1
- package/dist/translations/pt-PT/media.js +1 -1
- package/dist/translations/pt-PT/retrieval.js +1 -1
- package/dist/translations/pt-PT/shared.js +1 -1
- package/dist/translations/pt-PT/utility.js +1 -1
- package/dist/translations/pt-PT/viewers.js +2 -2
- package/dist/translations/ro/agent-tools.js +1 -1
- package/dist/translations/ru/agent-tools.js +1 -1
- package/dist/translations/sl/agent-tools.js +1 -1
- package/dist/translations/sv/agent-tools.js +1 -1
- package/dist/translations/tr/agent-tools.js +1 -1
- package/dist/translations/uk/agent-tools.js +1 -1
- package/dist/translations/zh-CN/agent-tools.js +1 -1
- package/dist/translations/zh-TW/agent-tools.js +1 -1
- package/dist/utilities/index.d.ts +1 -1
- package/dist/utilities/index.js +1 -1
- package/dist/utilities/localization.d.ts +28 -10
- package/dist/vue.d.ts +1 -1
- package/llms/components/lr-accordion-item.md +2 -344
- package/llms/components/lr-accordion.md +2 -344
- package/llms/components/lr-app-rail-group.md +2 -721
- package/llms/components/lr-app-rail-item.md +2 -721
- package/llms/components/lr-app-rail.md +29 -4
- package/llms/components/lr-bar-chart.md +2 -87
- package/llms/components/lr-box-plot.md +1 -1
- package/llms/components/lr-breadcrumb-item.md +2 -67
- package/llms/components/lr-bubble-chart.md +2 -87
- package/llms/components/lr-button.md +7 -0
- package/llms/components/lr-chart.md +4 -5
- package/llms/components/lr-chip-group.md +2 -272
- package/llms/components/lr-conversation-item.md +20 -2
- package/llms/components/lr-date-input.md +2 -457
- package/llms/components/lr-date-picker.md +6 -2
- package/llms/components/lr-document-library.md +3 -3
- package/llms/components/lr-document-viewer.md +14 -5
- package/llms/components/lr-doughnut-chart.md +2 -87
- package/llms/components/lr-dropdown-item.md +2 -306
- package/llms/components/lr-dropdown.md +6 -0
- package/llms/components/lr-flag.md +9 -1
- package/llms/components/lr-flow-node.md +4 -1
- package/llms/components/lr-geojson-view.md +3 -90
- package/llms/components/lr-geojson-viewer.md +13 -5
- package/llms/components/lr-icon-button.md +4 -1
- package/llms/components/lr-icon.md +14 -6
- package/llms/components/lr-input.md +9 -2
- package/llms/components/lr-lite-chart.md +17 -9
- package/llms/components/lr-map.md +87 -2
- package/llms/components/lr-markdown-core.md +5 -4
- package/llms/components/lr-markdown.md +4 -2
- package/llms/components/lr-media-card.md +10 -5
- package/llms/components/lr-menu-item.md +2 -306
- package/llms/components/lr-menu.md +9 -6
- package/llms/components/lr-menubar-item.md +2 -125
- package/llms/components/lr-message-actions.md +4 -1
- package/llms/components/lr-message-parts.md +14 -5
- package/llms/components/lr-mutation-observer.md +12 -6
- package/llms/components/lr-navigation-menu-item.md +2 -237
- package/llms/components/lr-number-input.md +4 -0
- package/llms/components/lr-option.md +2 -712
- package/llms/components/lr-pie-chart.md +2 -87
- package/llms/components/lr-polar-area-chart.md +2 -87
- package/llms/components/lr-popover.md +10 -0
- package/llms/components/lr-prompt-input.md +5 -1
- package/llms/components/lr-radar-chart.md +2 -87
- package/llms/components/lr-reorder-item.md +2 -157
- package/llms/components/lr-result-field.md +2 -117
- package/llms/components/lr-retrieval-results.md +3 -1
- package/llms/components/lr-rubric-form.md +3 -2
- package/llms/components/lr-scatter-chart.md +2 -87
- package/llms/components/lr-selection-toolbar.md +4 -2
- package/llms/components/lr-source-card.md +2 -186
- package/llms/components/lr-source-picker.md +3 -1
- package/llms/components/lr-sparkline.md +9 -4
- package/llms/components/lr-split-panel.md +5 -3
- package/llms/components/lr-stat.md +4 -3
- package/llms/components/lr-streaming-text-core.md +7 -5
- package/llms/components/lr-streaming-text.md +9 -6
- package/llms/components/lr-swatch-picker.md +130 -0
- package/llms/components/lr-tag.md +2 -177
- package/llms/components/lr-thread-list.md +27 -4
- package/llms/components/lr-timeline-item.md +2 -120
- package/llms/components/lr-toast-item.md +2 -223
- package/llms/components/lr-tool-call-block.md +12 -3
- package/llms/components/lr-tool-call-chip.md +12 -6
- package/llms/components/lr-tool-param-form.md +3 -2
- package/llms/components/lr-tool-result-dialog.md +7 -3
- package/llms/components/lr-tool-result-view.md +4 -3
- package/llms/components/lr-tool-timeline.md +12 -5
- package/llms/components/lr-tree-item.md +2 -354
- package/llms/index.md +22 -4
- package/llms/migration.md +75 -1
- package/llms/peers.md +1 -1
- package/llms/shared.md +86 -39
- package/llms-full.txt +739 -163
- package/package.json +6 -6
- package/registrations.json +1 -1
- package/vscode-css-data.json +1 -1
- package/vscode-html-data.json +1 -1
- package/web-types.json +1 -1
package/dist/cli/migrate-wa.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import fs from"node:fs";import path from"node:path";import{fileURLToPath}from"node:url";import{LOCAL_MIGRATION_ORIGINS,compareMappedSurfaces,validateAccessibilityContract,validateLocalMigrations,validateMappingNormalizations,validateMethodEdgeParity}from"./component-inventory.mjs";const scriptDir=path.dirname(fileURLToPath(import.meta.url)),packageDir=path.resolve(scriptDir,".."),packagedInventoryPath=path.join(scriptDir,"migration-contract.json"),inventoryPath=fs.existsSync(packagedInventoryPath)?packagedInventoryPath:path.join(packageDir,"scripts","fixtures","component-inventory.json"),MIGRATION_REPORT_SCHEMA_VERSION=1,DEFAULT_EXTENSIONS=new Set(["html","htm","ts","tsx","js","jsx","mjs","cjs","css","vue","svelte","mdx","md"]),IGNORE_DIR_NAMES=new Set(["node_modules","dist","build","coverage",".turbo",".cache"]),AUTO_CLASSIFICATIONS=new Set(["exact","rewritten"]),CLASSIFICATIONS=new Set(["exact","rewritten","warning-required","conceptual-only","unsupported"]),MEMBER_RULE_SECTIONS=["attributes","properties","events","slots","parts","cssProperties","methods"],REWRITE_RULE_SECTIONS=[...MEMBER_RULE_SECTIONS,"defaults"],ECOSYSTEMS=["webawesome","shoelace"],PACKAGE_TIERS=new Set(["free","pro"]),STATIC_API_STATUSES=new Set(["reviewed","tag-only","unreviewed"]),LIGHT_DOM_STATUSES=new Set(["not-applicable","surface-only","warning-required","unreviewed"]),REGISTRATION_STATUSES=new Set(["all","granular","unavailable"]),CONDITIONAL_BEHAVIOR_REVIEW_TAGS=new Set(["wa-random-content"]),REDUCED_MOTION_REVIEW_TAGS=new Set(["wa-animation","sl-animation","wa-animated-image","sl-animated-image"]),ICON_VOCABULARY_REVIEW_TAGS=new Set(["wa-icon","sl-icon","sl-icon-button"]);function reducedMotionReviewMessage(upstreamTag){return`${upstreamTag} does not freeze or snap its animation under prefers-reduced-motion: reduce. The migrated Lyra target defaults respect-reduced-motion to true and does exactly that automatically. Review whether the migrated element should keep animating for users who asked for less motion, or set respect-reduced-motion="false" to preserve upstream playback under that preference.`}function iconVocabularyReviewMessage(upstreamTag){return`${upstreamTag}'s icon-name vocabulary is not bundled with Lyra. The default library includes only add, check, close, search, menu, chevron-left, chevron-right, chevron-down, calendar, command, and trash; any other migrated name renders no glyph. Register the required vocabulary with registerIconLibrary('default', { resolver }) or replace each name explicitly.`}function invariant(condition,message){if(!condition)throw new Error(`Invalid component inventory migration contract: ${message}`)}function surfaceNames(component,section){return new Set((component?.surface?.[section]??[]).map(entry=>entry.name))}function isScalar(value){return value===null||typeof value=="string"||typeof value=="boolean"||typeof value=="number"&&Number.isFinite(value)}function validateMemberRules(mapping,source,target,section){const rules=mapping.rewrites[section];invariant(Array.isArray(rules),`${mapping.upstreamTag}: rewrites.${section} must be an array`);const sourceNames=surfaceNames(source,section),targetNames=surfaceNames(target,section),seen=new Set;for(const rule of rules){invariant(rule&&typeof rule.from=="string"&&rule.from&&typeof rule.to=="string"&&rule.to,`${mapping.upstreamTag}: every rewrites.${section} rule needs non-empty from/to`);const unknownKeys=Object.keys(rule).filter(key=>key!=="from"&&key!=="to");invariant(unknownKeys.length===0,`${mapping.upstreamTag}: rewrites.${section} rule has unknown key(s) ${unknownKeys.join(", ")}`),invariant(rule.from!==rule.to,`${mapping.upstreamTag}: rewrites.${section} cannot map a member to itself`),invariant(!seen.has(rule.from),`${mapping.upstreamTag}: duplicate rewrites.${section} source ${rule.from}`),seen.add(rule.from),invariant(sourceNames.has(rule.from),`${mapping.upstreamTag}: unknown ${section} source ${rule.from}`),invariant(targetNames.has(rule.to),`${mapping.upstreamTag}: unknown target ${section} member ${rule.to}`)}}function validateDefaultRules(mapping,source,target){const rules=mapping.rewrites.defaults;invariant(Array.isArray(rules),`${mapping.upstreamTag}: rewrites.defaults must be an array`);const seen=new Set;for(const rule of rules){invariant(rule?.memberKind==="attribute"||rule?.memberKind==="property",`${mapping.upstreamTag}: default memberKind must be attribute or property`),invariant(typeof rule.member=="string"&&rule.member,`${mapping.upstreamTag}: default rule needs member`),invariant(rule.action==="insert-if-absent"||rule.action==="replace-value",`${mapping.upstreamTag}: unsupported default action ${String(rule.action)}`);const allowedKeys=rule.action==="insert-if-absent"?new Set(["memberKind","member","action","value"]):new Set(["memberKind","member","action","from","to"]),unknownKeys=Object.keys(rule).filter(key2=>!allowedKeys.has(key2));invariant(unknownKeys.length===0,`${mapping.upstreamTag}: default rule has unknown key(s) ${unknownKeys.join(", ")}`);const key=`${rule.memberKind}:${rule.member}:${rule.action}`;invariant(!seen.has(key),`${mapping.upstreamTag}: duplicate default rule ${key}`),seen.add(key);const section=rule.memberKind==="attribute"?"attributes":"properties";invariant(surfaceNames(target,section).has(rule.member),`${mapping.upstreamTag}: unknown target ${key}`),rule.action==="insert-if-absent"?(invariant(rule.memberKind==="attribute",`${mapping.upstreamTag}: insert-if-absent is only deterministic for attributes`),invariant(Object.hasOwn(rule,"value"),`${mapping.upstreamTag}: ${key} needs value`),invariant(isScalar(rule.value),`${mapping.upstreamTag}: ${key} value must be a scalar`),invariant(!Object.hasOwn(rule,"from")&&!Object.hasOwn(rule,"to"),`${mapping.upstreamTag}: ${key} cannot use from/to`)):(invariant(surfaceNames(source,section).has(rule.member),`${mapping.upstreamTag}: unknown source ${key}`),invariant(Object.hasOwn(rule,"from")&&Object.hasOwn(rule,"to"),`${mapping.upstreamTag}: ${key} needs from/to`),invariant(isScalar(rule.from)&&isScalar(rule.to),`${mapping.upstreamTag}: ${key} from/to must be scalars`),invariant(rule.from!==rule.to,`${mapping.upstreamTag}: ${key} cannot replace a value with itself`),invariant(!Object.hasOwn(rule,"value"),`${mapping.upstreamTag}: ${key} cannot use value`))}}function driftCovered(mapping,finding){const codeToSection={"missing-attribute":"attributes","polarity-mismatch":"attributes","missing-property":"properties","missing-event":"events","missing-slot":"slots","missing-part":"parts","missing-css-property":"cssProperties","missing-method":"methods"};if(finding.code==="default-mismatch")return mapping.rewrites.defaults.some(rule=>rule.member===finding.member);const section=codeToSection[finding.code];return!!(section&&mapping.rewrites[section].some(rule=>rule.from===finding.member))}function runtimeMemberSurfaces(mapping,section){const rules=Array.isArray(mapping.rewrites?.[section])?mapping.rewrites[section]:[];return{source:{surface:{[section]:rules.map(rule=>({name:rule?.from}))}},target:{surface:{[section]:rules.map(rule=>({name:rule?.to}))}}}}function runtimeDefaultSurfaces(mapping){const source={surface:{attributes:[],properties:[]}},target={surface:{attributes:[],properties:[]}},rules=Array.isArray(mapping.rewrites?.defaults)?mapping.rewrites.defaults:[];for(const rule of rules){const section=rule?.memberKind==="attribute"?"attributes":"properties";target.surface[section].push({name:rule?.member}),rule?.action==="replace-value"&&source.surface[section].push({name:rule?.member})}return{source,target}}function buildMigrationContract(inventory){invariant(inventory?.schemaVersion===1,"schemaVersion must be 1");const runtimeInventory=Object.hasOwn(inventory,"migrationRuntimeSchemaVersion");runtimeInventory&&invariant(inventory.migrationRuntimeSchemaVersion===1,"migrationRuntimeSchemaVersion must be 1"),invariant(Array.isArray(inventory.components),"components must be an array"),invariant(Array.isArray(inventory.mappings),"mappings must be an array"),invariant(inventory.upstreams&&typeof inventory.upstreams=="object","upstreams must be an object");const accessibilityFindings=validateAccessibilityContract(inventory.accessibilityProfiles,inventory.mappings);invariant(accessibilityFindings.length===0,accessibilityFindings.join("; "));const components=new Map;for(const component of inventory.components)invariant(typeof component.tag=="string"&&component.tag,"every Lyra component needs a tag"),invariant(!components.has(component.tag),`duplicate Lyra component ${component.tag}`),invariant(typeof component.registrationModule=="string"&&component.registrationModule.endsWith(".ts"),`${component.tag}: registrationModule must end in .ts`),Object.hasOwn(component,"rootIncluded")&&invariant(typeof component.rootIncluded=="boolean",`${component.tag}: rootIncluded must be boolean`),Object.hasOwn(component,"optionalPeers")&&(invariant(Array.isArray(component.optionalPeers),`${component.tag}: optionalPeers must be an array`),invariant(component.optionalPeers.every(peer=>typeof peer=="string"&&peer),`${component.tag}: optionalPeers entries must be non-empty package names`)),components.set(component.tag,component);const localMigrationFindings=validateLocalMigrations(inventory);invariant(localMigrationFindings.length===0,localMigrationFindings.join("; "));const localMigrations=new Map(LOCAL_MIGRATION_ORIGINS.map(origin=>[origin,new Map]));for(const profile of inventory.localMigrations)localMigrations.get(profile.origin).set(profile.tag,profile);const upstreamComponents=new Map,packageIdentities=new Map,packagesByEcosystem=new Map(ECOSYSTEMS.map(ecosystem=>[ecosystem,[]]));let extendedPackageSchema=!1;for(const ecosystem of ECOSYSTEMS){const upstream=inventory.upstreams[ecosystem],entries=upstream?.components;invariant(Array.isArray(entries),`${ecosystem}: upstream components must be an array`);const identities=Array.isArray(upstream?.packages)?upstream.packages:typeof upstream?.package=="string"&&upstream.package?[{name:upstream.package,tiers:["free","pro"]}]:[];Array.isArray(upstream?.packages)&&(extendedPackageSchema=!0),invariant(identities.length>0,`${ecosystem}: packages must contain at least one identity`);for(const identity of identities){invariant(identity&&typeof identity.name=="string"&&identity.name,`${ecosystem}: package identity needs a non-empty name`),invariant(!packageIdentities.has(identity.name),`duplicate package identity ${identity.name}`),invariant(Array.isArray(identity.tiers)&&identity.tiers.length>0,`${identity.name}: tiers must be a non-empty array`),invariant(identity.tiers.every(tier=>PACKAGE_TIERS.has(tier)),`${identity.name}: tiers contain an unsupported value`),invariant(new Set(identity.tiers).size===identity.tiers.length,`${identity.name}: tiers contain duplicates`);const normalized={ecosystem,tiers:new Set(identity.tiers)};packageIdentities.set(identity.name,normalized),packagesByEcosystem.get(ecosystem).push({name:identity.name,tiers:normalized.tiers})}for(const component of entries)invariant(!upstreamComponents.has(component.tag),`duplicate upstream component ${component.tag}`),Object.hasOwn(component,"tier")&&(invariant(PACKAGE_TIERS.has(component.tier),`${component.tag}: unsupported package tier`),invariant(packagesByEcosystem.get(ecosystem).some(identity=>identity.tiers.has(component.tier)),`${component.tag}: no ${ecosystem} package identity provides tier ${component.tier}`)),upstreamComponents.set(component.tag,{ecosystem,component})}const mappings=new Map;for(const mapping of inventory.mappings){invariant(typeof mapping.upstreamTag=="string"&&mapping.upstreamTag,"every mapping needs upstreamTag"),invariant(!mappings.has(mapping.upstreamTag),`duplicate mapping ${mapping.upstreamTag}`),invariant(CLASSIFICATIONS.has(mapping.classification),`${mapping.upstreamTag}: invalid classification`),invariant(mapping.upstream==="webawesome"||mapping.upstream==="shoelace",`${mapping.upstreamTag}: invalid upstream`);const upstreamEntry=upstreamComponents.get(mapping.upstreamTag);invariant(upstreamEntry?.ecosystem===mapping.upstream,`${mapping.upstreamTag}: missing upstream surface`);const target=components.get(mapping.targetTag);AUTO_CLASSIFICATIONS.has(mapping.classification)&&invariant(target,`${mapping.upstreamTag}: automatic mapping needs a registered target`),invariant(mapping.rewrites&&typeof mapping.rewrites=="object",`${mapping.upstreamTag}: rewrites missing`);const unknownRewriteKeys=Object.keys(mapping.rewrites).filter(key=>!REWRITE_RULE_SECTIONS.includes(key));invariant(unknownRewriteKeys.length===0,`${mapping.upstreamTag}: unknown rewrite section(s) ${unknownRewriteKeys.join(", ")}`);for(const section of MEMBER_RULE_SECTIONS)if(runtimeInventory){const surfaces=runtimeMemberSurfaces(mapping,section);validateMemberRules(mapping,surfaces.source,surfaces.target,section)}else validateMemberRules(mapping,upstreamEntry.component,target,section);if(runtimeInventory){const surfaces=runtimeDefaultSurfaces(mapping);validateDefaultRules(mapping,surfaces.source,surfaces.target),invariant(!Object.hasOwn(mapping,"normalizations")&&!Object.hasOwn(mapping,"drift"),`${mapping.upstreamTag}: packaged runtime mappings cannot carry analyzer-only data`)}else{validateDefaultRules(mapping,upstreamEntry.component,target);const normalizationFindings=validateMappingNormalizations(mapping,{upstream:upstreamEntry.component.surface,target:target?.surface});invariant(normalizationFindings.length===0,normalizationFindings.join("; ")),invariant(Array.isArray(mapping.drift),`${mapping.upstreamTag}: drift must be an array`)}if(extendedPackageSchema){const parity=mapping.parity;invariant(parity&&typeof parity=="object"&&!Array.isArray(parity),`${mapping.upstreamTag}: parity missing`);const parityKeys=Object.keys(parity).filter(key=>!["staticApi","lightDom","runtime","behaviorReviewFlags","accessibility","methodEdges"].includes(key));invariant(parityKeys.length===0,`${mapping.upstreamTag}: parity has unknown key(s) ${parityKeys.join(", ")}`),invariant(STATIC_API_STATUSES.has(parity.staticApi),`${mapping.upstreamTag}: invalid parity.staticApi`),invariant(LIGHT_DOM_STATUSES.has(parity.lightDom),`${mapping.upstreamTag}: invalid parity.lightDom`),invariant(Array.isArray(parity.behaviorReviewFlags)&&parity.behaviorReviewFlags.every(flag=>typeof flag=="string"&&flag)&&new Set(parity.behaviorReviewFlags).size===parity.behaviorReviewFlags.length,`${mapping.upstreamTag}: parity.behaviorReviewFlags must contain unique non-empty strings`),invariant(parity.runtime&&typeof parity.runtime=="object"&&!Array.isArray(parity.runtime),`${mapping.upstreamTag}: parity.runtime missing`),invariant(Object.keys(parity.runtime).every(key=>key==="registration"||key==="optionalPeers"),`${mapping.upstreamTag}: parity.runtime has unknown keys`),invariant(REGISTRATION_STATUSES.has(parity.runtime.registration),`${mapping.upstreamTag}: invalid parity.runtime.registration`),invariant(Array.isArray(parity.runtime.optionalPeers),`${mapping.upstreamTag}: parity.runtime.optionalPeers must be an array`);const expectedRegistration=target?target.rootIncluded===!1?"granular":"all":"unavailable";invariant(parity.runtime.registration===expectedRegistration,`${mapping.upstreamTag}: parity runtime registration is stale`);const expectedPeers=[...target?.optionalPeers??[]].sort();invariant(JSON.stringify([...parity.runtime.optionalPeers].sort())===JSON.stringify(expectedPeers),`${mapping.upstreamTag}: parity runtime optional peers are stale`);const methodEdgeFindings=validateMethodEdgeParity(mapping,runtimeInventory?{}:{upstream:upstreamEntry.component.surface,target:target?.surface});invariant(methodEdgeFindings.length===0,methodEdgeFindings.join("; ")),AUTO_CLASSIFICATIONS.has(mapping.classification)&&(parity.behaviorReviewFlags.length>0?invariant(mapping.classification==="rewritten"&&CONDITIONAL_BEHAVIOR_REVIEW_TAGS.has(mapping.upstreamTag)&&parity.lightDom==="warning-required",`${mapping.upstreamTag}: automatic behavior review lacks a registered conditional scanner`):invariant(parity.lightDom!=="warning-required"&&parity.lightDom!=="unreviewed",`${mapping.upstreamTag}: automatic mapping cannot require unscoped light-DOM review`))}if(!runtimeInventory&&target&&upstreamEntry.component.review?.status==="complete"){const expectedDrift=compareMappedSurfaces(upstreamEntry.component.surface,target.surface,{upstreamPrefix:mapping.upstream==="webawesome"?"wa-":"sl-",rewrites:mapping.rewrites,normalizations:mapping.normalizations});invariant(JSON.stringify(mapping.drift)===JSON.stringify(expectedDrift),`${mapping.upstreamTag}: stored surface drift is stale`)}mapping.classification==="exact"&&(runtimeInventory||invariant(mapping.drift.length===0,`${mapping.upstreamTag}: exact mapping has surface drift`),invariant(REWRITE_RULE_SECTIONS.every(section=>mapping.rewrites[section].length===0),`${mapping.upstreamTag}: exact mapping cannot declare rewrite rules`)),mapping.classification==="rewritten"&&(invariant(REWRITE_RULE_SECTIONS.some(section=>mapping.rewrites[section].length>0),`${mapping.upstreamTag}: rewritten mapping needs at least one deterministic rule`),runtimeInventory||invariant(mapping.drift.every(finding=>driftCovered(mapping,finding)),`${mapping.upstreamTag}: rewritten mapping contains drift without a deterministic rule`)),mappings.set(mapping.upstreamTag,{...mapping,drift:mapping.drift??[],source:upstreamEntry.component,target})}invariant(mappings.size===upstreamComponents.size,"every upstream tag must have exactly one mapping");for(const tag of upstreamComponents.keys())invariant(mappings.has(tag),`${tag}: missing mapping`);return{inventory,components,mappings,upstreamComponents,localMigrations,packageIdentities,packagesByEcosystem}}function createMigrationRuntimeInventory(inventory){invariant(!Object.hasOwn(inventory??{},"migrationRuntimeSchemaVersion"),"cannot project an already-packaged migration runtime inventory"),buildMigrationContract(inventory);const targetTags=new Set(inventory.mappings.map(mapping=>mapping.targetTag).filter(Boolean));for(const profile of inventory.localMigrations)targetTags.add(profile.tag);return{schemaVersion:1,migrationRuntimeSchemaVersion:1,accessibilityProfiles:structuredClone(inventory.accessibilityProfiles),components:inventory.components.filter(component=>targetTags.has(component.tag)).map(component=>({tag:component.tag,registrationModule:component.registrationModule,rootIncluded:component.rootIncluded,optionalPeers:structuredClone(component.optionalPeers??[]),surface:{attributes:structuredClone(component.surface?.attributes??[])}})),localMigrations:structuredClone(inventory.localMigrations),upstreams:Object.fromEntries(ECOSYSTEMS.map(ecosystem=>{const upstream=inventory.upstreams[ecosystem];return[ecosystem,{packages:structuredClone(upstream.packages),components:upstream.components.map(component=>({tag:component.tag,tier:component.tier}))}]})),mappings:inventory.mappings.map(mapping=>({upstream:mapping.upstream,upstreamTag:mapping.upstreamTag,targetTag:mapping.targetTag,classification:mapping.classification,rationale:mapping.rationale,parity:structuredClone(mapping.parity),rewrites:structuredClone(mapping.rewrites)}))}}function buildMirrorMap(readmeText){const map=new Map,conflicts=[];let mode=null;const setMapping=(from,to)=>{map.has(from)&&map.get(from)!==to?conflicts.push(`${from}: already mapped to ${map.get(from)}, also saw ${to}`):map.set(from,to)};for(const rawLine of readmeText.split(`
|
|
2
|
+
import fs from"node:fs";import path from"node:path";import{fileURLToPath}from"node:url";import{LOCAL_MIGRATION_ORIGINS,compareMappedSurfaces,validateAccessibilityContract,validateLocalMigrations,validateMappingNormalizations,validateMethodEdgeParity}from"./component-inventory.mjs";import{LYRA_RENAME_ORIGINS,compareVersions,createRenameProfiles,emptyRenameProjection,projectRenameLedger,validateRenameLedgerShape}from"./lyra-rename-ledger.mjs";const scriptDir=path.dirname(fileURLToPath(import.meta.url)),packageDir=path.resolve(scriptDir,".."),packagedInventoryPath=path.join(scriptDir,"migration-contract.json"),packagedRuntime=fs.existsSync(packagedInventoryPath),inventoryPath=packagedRuntime?packagedInventoryPath:path.join(packageDir,"scripts","fixtures","component-inventory.json"),renameLedgerPath=path.join(packageDir,"scripts","fixtures","lyra-renames.json"),MIGRATION_REPORT_SCHEMA_VERSION=1,MIGRATION_RUNTIME_SCHEMA_VERSION=2,MIGRATION_ORIGINS=Object.freeze([...LOCAL_MIGRATION_ORIGINS,...LYRA_RENAME_ORIGINS]),DEFAULT_EXTENSIONS=new Set(["html","htm","ts","tsx","js","jsx","mjs","cjs","css","vue","svelte","mdx","md"]),IGNORE_DIR_NAMES=new Set(["node_modules","dist","build","coverage",".turbo",".cache"]),AUTO_CLASSIFICATIONS=new Set(["exact","rewritten"]),CLASSIFICATIONS=new Set(["exact","rewritten","warning-required","conceptual-only","unsupported"]),MEMBER_RULE_SECTIONS=["attributes","properties","events","slots","parts","cssProperties","methods"],REWRITE_RULE_SECTIONS=[...MEMBER_RULE_SECTIONS,"defaults"],ECOSYSTEMS=["webawesome","shoelace"],PACKAGE_TIERS=new Set(["free","pro"]),STATIC_API_STATUSES=new Set(["reviewed","tag-only","unreviewed"]),LIGHT_DOM_STATUSES=new Set(["not-applicable","surface-only","warning-required","unreviewed"]),REGISTRATION_STATUSES=new Set(["all","granular","unavailable"]),CONDITIONAL_BEHAVIOR_REVIEW_TAGS=new Set(["wa-random-content"]),REDUCED_MOTION_REVIEW_TAGS=new Set(["wa-animation","sl-animation","wa-animated-image","sl-animated-image"]),ICON_VOCABULARY_REVIEW_TAGS=new Set(["wa-icon","sl-icon","sl-icon-button"]);function reducedMotionReviewMessage(upstreamTag){return`${upstreamTag} does not freeze or snap its animation under prefers-reduced-motion: reduce. The migrated Lyra target defaults respect-reduced-motion to true and does exactly that automatically. Review whether the migrated element should keep animating for users who asked for less motion, or set respect-reduced-motion="false" to preserve upstream playback under that preference.`}function iconVocabularyReviewMessage(upstreamTag){return`${upstreamTag}'s icon-name vocabulary is not bundled with Lyra. The default library includes only add, check, close, search, menu, chevron-left, chevron-right, chevron-down, calendar, command, and trash; any other migrated name renders no glyph. Register the required vocabulary with registerIconLibrary('default', { resolver }) or replace each name explicitly.`}function invariant(condition,message){if(!condition)throw new Error(`Invalid component inventory migration contract: ${message}`)}function surfaceNames(component,section){return new Set((component?.surface?.[section]??[]).map(entry=>entry.name))}function isScalar(value){return value===null||typeof value=="string"||typeof value=="boolean"||typeof value=="number"&&Number.isFinite(value)}function validateMemberRules(mapping,source,target,section){const rules=mapping.rewrites[section];invariant(Array.isArray(rules),`${mapping.upstreamTag}: rewrites.${section} must be an array`);const sourceNames=surfaceNames(source,section),targetNames=surfaceNames(target,section),seen=new Set;for(const rule of rules){invariant(rule&&typeof rule.from=="string"&&rule.from&&typeof rule.to=="string"&&rule.to,`${mapping.upstreamTag}: every rewrites.${section} rule needs non-empty from/to`);const unknownKeys=Object.keys(rule).filter(key=>key!=="from"&&key!=="to");invariant(unknownKeys.length===0,`${mapping.upstreamTag}: rewrites.${section} rule has unknown key(s) ${unknownKeys.join(", ")}`),invariant(rule.from!==rule.to,`${mapping.upstreamTag}: rewrites.${section} cannot map a member to itself`),invariant(!seen.has(rule.from),`${mapping.upstreamTag}: duplicate rewrites.${section} source ${rule.from}`),seen.add(rule.from),invariant(sourceNames.has(rule.from),`${mapping.upstreamTag}: unknown ${section} source ${rule.from}`),invariant(targetNames.has(rule.to),`${mapping.upstreamTag}: unknown target ${section} member ${rule.to}`)}}function validateDefaultRules(mapping,source,target){const rules=mapping.rewrites.defaults;invariant(Array.isArray(rules),`${mapping.upstreamTag}: rewrites.defaults must be an array`);const seen=new Set;for(const rule of rules){invariant(rule?.memberKind==="attribute"||rule?.memberKind==="property",`${mapping.upstreamTag}: default memberKind must be attribute or property`),invariant(typeof rule.member=="string"&&rule.member,`${mapping.upstreamTag}: default rule needs member`),invariant(rule.action==="insert-if-absent"||rule.action==="replace-value",`${mapping.upstreamTag}: unsupported default action ${String(rule.action)}`);const allowedKeys=rule.action==="insert-if-absent"?new Set(["memberKind","member","action","value"]):new Set(["memberKind","member","action","from","to"]),unknownKeys=Object.keys(rule).filter(key2=>!allowedKeys.has(key2));invariant(unknownKeys.length===0,`${mapping.upstreamTag}: default rule has unknown key(s) ${unknownKeys.join(", ")}`);const key=`${rule.memberKind}:${rule.member}:${rule.action}`;invariant(!seen.has(key),`${mapping.upstreamTag}: duplicate default rule ${key}`),seen.add(key);const section=rule.memberKind==="attribute"?"attributes":"properties";invariant(surfaceNames(target,section).has(rule.member),`${mapping.upstreamTag}: unknown target ${key}`),rule.action==="insert-if-absent"?(invariant(rule.memberKind==="attribute",`${mapping.upstreamTag}: insert-if-absent is only deterministic for attributes`),invariant(Object.hasOwn(rule,"value"),`${mapping.upstreamTag}: ${key} needs value`),invariant(isScalar(rule.value),`${mapping.upstreamTag}: ${key} value must be a scalar`),invariant(!Object.hasOwn(rule,"from")&&!Object.hasOwn(rule,"to"),`${mapping.upstreamTag}: ${key} cannot use from/to`)):(invariant(surfaceNames(source,section).has(rule.member),`${mapping.upstreamTag}: unknown source ${key}`),invariant(Object.hasOwn(rule,"from")&&Object.hasOwn(rule,"to"),`${mapping.upstreamTag}: ${key} needs from/to`),invariant(isScalar(rule.from)&&isScalar(rule.to),`${mapping.upstreamTag}: ${key} from/to must be scalars`),invariant(rule.from!==rule.to,`${mapping.upstreamTag}: ${key} cannot replace a value with itself`),invariant(!Object.hasOwn(rule,"value"),`${mapping.upstreamTag}: ${key} cannot use value`))}}function driftCovered(mapping,finding){const codeToSection={"missing-attribute":"attributes","polarity-mismatch":"attributes","missing-property":"properties","missing-event":"events","missing-slot":"slots","missing-part":"parts","missing-css-property":"cssProperties","missing-method":"methods"};if(finding.code==="default-mismatch")return mapping.rewrites.defaults.some(rule=>rule.member===finding.member);const section=codeToSection[finding.code];return!!(section&&mapping.rewrites[section].some(rule=>rule.from===finding.member))}function runtimeMemberSurfaces(mapping,section){const rules=Array.isArray(mapping.rewrites?.[section])?mapping.rewrites[section]:[];return{source:{surface:{[section]:rules.map(rule=>({name:rule?.from}))}},target:{surface:{[section]:rules.map(rule=>({name:rule?.to}))}}}}function runtimeDefaultSurfaces(mapping){const source={surface:{attributes:[],properties:[]}},target={surface:{attributes:[],properties:[]}},rules=Array.isArray(mapping.rewrites?.defaults)?mapping.rewrites.defaults:[];for(const rule of rules){const section=rule?.memberKind==="attribute"?"attributes":"properties";target.surface[section].push({name:rule?.member}),rule?.action==="replace-value"&&source.surface[section].push({name:rule?.member})}return{source,target}}function buildMigrationContract(inventory,{renameLedger=null,lyraVersion=null}={}){invariant(inventory?.schemaVersion===1,"schemaVersion must be 1");const runtimeInventory=Object.hasOwn(inventory,"migrationRuntimeSchemaVersion");if(runtimeInventory){invariant(inventory.migrationRuntimeSchemaVersion===MIGRATION_RUNTIME_SCHEMA_VERSION,`migrationRuntimeSchemaVersion must be ${MIGRATION_RUNTIME_SCHEMA_VERSION}`),invariant(renameLedger===null,"a packaged runtime inventory carries its own rename projection");const renameFindings=validateRenameLedgerShape(inventory.lyraRenames,{projected:!0});invariant(renameFindings.length===0,renameFindings.join("; "))}const renameProfiles=createRenameProfiles(runtimeInventory?inventory.lyraRenames:renameLedger?projectRenameLedger(renameLedger,inventory):emptyRenameProjection(),{lyraVersion});invariant(Array.isArray(inventory.components),"components must be an array"),invariant(Array.isArray(inventory.mappings),"mappings must be an array"),invariant(inventory.upstreams&&typeof inventory.upstreams=="object","upstreams must be an object");const accessibilityFindings=validateAccessibilityContract(inventory.accessibilityProfiles,inventory.mappings);invariant(accessibilityFindings.length===0,accessibilityFindings.join("; "));const components=new Map;for(const component of inventory.components)invariant(typeof component.tag=="string"&&component.tag,"every Lyra component needs a tag"),invariant(!components.has(component.tag),`duplicate Lyra component ${component.tag}`),invariant(typeof component.registrationModule=="string"&&component.registrationModule.endsWith(".ts"),`${component.tag}: registrationModule must end in .ts`),Object.hasOwn(component,"rootIncluded")&&invariant(typeof component.rootIncluded=="boolean",`${component.tag}: rootIncluded must be boolean`),Object.hasOwn(component,"optionalPeers")&&(invariant(Array.isArray(component.optionalPeers),`${component.tag}: optionalPeers must be an array`),invariant(component.optionalPeers.every(peer=>typeof peer=="string"&&peer),`${component.tag}: optionalPeers entries must be non-empty package names`)),components.set(component.tag,component);const localMigrationFindings=validateLocalMigrations(inventory);invariant(localMigrationFindings.length===0,localMigrationFindings.join("; "));const localMigrations=new Map(LOCAL_MIGRATION_ORIGINS.map(origin=>[origin,new Map]));for(const profile of inventory.localMigrations)localMigrations.get(profile.origin).set(profile.tag,profile);const upstreamComponents=new Map,packageIdentities=new Map,packagesByEcosystem=new Map(ECOSYSTEMS.map(ecosystem=>[ecosystem,[]]));let extendedPackageSchema=!1;for(const ecosystem of ECOSYSTEMS){const upstream=inventory.upstreams[ecosystem],entries=upstream?.components;invariant(Array.isArray(entries),`${ecosystem}: upstream components must be an array`);const identities=Array.isArray(upstream?.packages)?upstream.packages:typeof upstream?.package=="string"&&upstream.package?[{name:upstream.package,tiers:["free","pro"]}]:[];Array.isArray(upstream?.packages)&&(extendedPackageSchema=!0),invariant(identities.length>0,`${ecosystem}: packages must contain at least one identity`);for(const identity of identities){invariant(identity&&typeof identity.name=="string"&&identity.name,`${ecosystem}: package identity needs a non-empty name`),invariant(!packageIdentities.has(identity.name),`duplicate package identity ${identity.name}`),invariant(Array.isArray(identity.tiers)&&identity.tiers.length>0,`${identity.name}: tiers must be a non-empty array`),invariant(identity.tiers.every(tier=>PACKAGE_TIERS.has(tier)),`${identity.name}: tiers contain an unsupported value`),invariant(new Set(identity.tiers).size===identity.tiers.length,`${identity.name}: tiers contain duplicates`);const normalized={ecosystem,tiers:new Set(identity.tiers)};packageIdentities.set(identity.name,normalized),packagesByEcosystem.get(ecosystem).push({name:identity.name,tiers:normalized.tiers})}for(const component of entries)invariant(!upstreamComponents.has(component.tag),`duplicate upstream component ${component.tag}`),Object.hasOwn(component,"tier")&&(invariant(PACKAGE_TIERS.has(component.tier),`${component.tag}: unsupported package tier`),invariant(packagesByEcosystem.get(ecosystem).some(identity=>identity.tiers.has(component.tier)),`${component.tag}: no ${ecosystem} package identity provides tier ${component.tier}`)),upstreamComponents.set(component.tag,{ecosystem,component})}const mappings=new Map;for(const mapping of inventory.mappings){invariant(typeof mapping.upstreamTag=="string"&&mapping.upstreamTag,"every mapping needs upstreamTag"),invariant(!mappings.has(mapping.upstreamTag),`duplicate mapping ${mapping.upstreamTag}`),invariant(CLASSIFICATIONS.has(mapping.classification),`${mapping.upstreamTag}: invalid classification`),invariant(mapping.upstream==="webawesome"||mapping.upstream==="shoelace",`${mapping.upstreamTag}: invalid upstream`);const upstreamEntry=upstreamComponents.get(mapping.upstreamTag);invariant(upstreamEntry?.ecosystem===mapping.upstream,`${mapping.upstreamTag}: missing upstream surface`);const target=components.get(mapping.targetTag);AUTO_CLASSIFICATIONS.has(mapping.classification)&&invariant(target,`${mapping.upstreamTag}: automatic mapping needs a registered target`),invariant(mapping.rewrites&&typeof mapping.rewrites=="object",`${mapping.upstreamTag}: rewrites missing`);const unknownRewriteKeys=Object.keys(mapping.rewrites).filter(key=>!REWRITE_RULE_SECTIONS.includes(key));invariant(unknownRewriteKeys.length===0,`${mapping.upstreamTag}: unknown rewrite section(s) ${unknownRewriteKeys.join(", ")}`);for(const section of MEMBER_RULE_SECTIONS)if(runtimeInventory){const surfaces=runtimeMemberSurfaces(mapping,section);validateMemberRules(mapping,surfaces.source,surfaces.target,section)}else validateMemberRules(mapping,upstreamEntry.component,target,section);if(runtimeInventory){const surfaces=runtimeDefaultSurfaces(mapping);validateDefaultRules(mapping,surfaces.source,surfaces.target),invariant(!Object.hasOwn(mapping,"normalizations")&&!Object.hasOwn(mapping,"drift"),`${mapping.upstreamTag}: packaged runtime mappings cannot carry analyzer-only data`)}else{validateDefaultRules(mapping,upstreamEntry.component,target);const normalizationFindings=validateMappingNormalizations(mapping,{upstream:upstreamEntry.component.surface,target:target?.surface});invariant(normalizationFindings.length===0,normalizationFindings.join("; ")),invariant(Array.isArray(mapping.drift),`${mapping.upstreamTag}: drift must be an array`)}if(extendedPackageSchema){const parity=mapping.parity;invariant(parity&&typeof parity=="object"&&!Array.isArray(parity),`${mapping.upstreamTag}: parity missing`);const parityKeys=Object.keys(parity).filter(key=>!["staticApi","lightDom","runtime","behaviorReviewFlags","accessibility","methodEdges"].includes(key));invariant(parityKeys.length===0,`${mapping.upstreamTag}: parity has unknown key(s) ${parityKeys.join(", ")}`),invariant(STATIC_API_STATUSES.has(parity.staticApi),`${mapping.upstreamTag}: invalid parity.staticApi`),invariant(LIGHT_DOM_STATUSES.has(parity.lightDom),`${mapping.upstreamTag}: invalid parity.lightDom`),invariant(Array.isArray(parity.behaviorReviewFlags)&&parity.behaviorReviewFlags.every(flag=>typeof flag=="string"&&flag)&&new Set(parity.behaviorReviewFlags).size===parity.behaviorReviewFlags.length,`${mapping.upstreamTag}: parity.behaviorReviewFlags must contain unique non-empty strings`),invariant(parity.runtime&&typeof parity.runtime=="object"&&!Array.isArray(parity.runtime),`${mapping.upstreamTag}: parity.runtime missing`),invariant(Object.keys(parity.runtime).every(key=>key==="registration"||key==="optionalPeers"),`${mapping.upstreamTag}: parity.runtime has unknown keys`),invariant(REGISTRATION_STATUSES.has(parity.runtime.registration),`${mapping.upstreamTag}: invalid parity.runtime.registration`),invariant(Array.isArray(parity.runtime.optionalPeers),`${mapping.upstreamTag}: parity.runtime.optionalPeers must be an array`);const expectedRegistration=target?target.rootIncluded===!1?"granular":"all":"unavailable";invariant(parity.runtime.registration===expectedRegistration,`${mapping.upstreamTag}: parity runtime registration is stale`);const expectedPeers=[...target?.optionalPeers??[]].sort();invariant(JSON.stringify([...parity.runtime.optionalPeers].sort())===JSON.stringify(expectedPeers),`${mapping.upstreamTag}: parity runtime optional peers are stale`);const methodEdgeFindings=validateMethodEdgeParity(mapping,runtimeInventory?{}:{upstream:upstreamEntry.component.surface,target:target?.surface});invariant(methodEdgeFindings.length===0,methodEdgeFindings.join("; ")),AUTO_CLASSIFICATIONS.has(mapping.classification)&&(parity.behaviorReviewFlags.length>0?invariant(mapping.classification==="rewritten"&&CONDITIONAL_BEHAVIOR_REVIEW_TAGS.has(mapping.upstreamTag)&&parity.lightDom==="warning-required",`${mapping.upstreamTag}: automatic behavior review lacks a registered conditional scanner`):invariant(parity.lightDom!=="warning-required"&&parity.lightDom!=="unreviewed",`${mapping.upstreamTag}: automatic mapping cannot require unscoped light-DOM review`))}if(!runtimeInventory&&target&&upstreamEntry.component.review?.status==="complete"){const expectedDrift=compareMappedSurfaces(upstreamEntry.component.surface,target.surface,{upstreamPrefix:mapping.upstream==="webawesome"?"wa-":"sl-",rewrites:mapping.rewrites,normalizations:mapping.normalizations});invariant(JSON.stringify(mapping.drift)===JSON.stringify(expectedDrift),`${mapping.upstreamTag}: stored surface drift is stale`)}mapping.classification==="exact"&&(runtimeInventory||invariant(mapping.drift.length===0,`${mapping.upstreamTag}: exact mapping has surface drift`),invariant(REWRITE_RULE_SECTIONS.every(section=>mapping.rewrites[section].length===0),`${mapping.upstreamTag}: exact mapping cannot declare rewrite rules`)),mapping.classification==="rewritten"&&(invariant(REWRITE_RULE_SECTIONS.some(section=>mapping.rewrites[section].length>0),`${mapping.upstreamTag}: rewritten mapping needs at least one deterministic rule`),runtimeInventory||invariant(mapping.drift.every(finding=>driftCovered(mapping,finding)),`${mapping.upstreamTag}: rewritten mapping contains drift without a deterministic rule`)),mappings.set(mapping.upstreamTag,{...mapping,drift:mapping.drift??[],source:upstreamEntry.component,target})}invariant(mappings.size===upstreamComponents.size,"every upstream tag must have exactly one mapping");for(const tag of upstreamComponents.keys())invariant(mappings.has(tag),`${tag}: missing mapping`);return{inventory,components,mappings,upstreamComponents,localMigrations,renameProfiles,packageIdentities,packagesByEcosystem}}function createMigrationRuntimeInventory(inventory,{renameLedger}={}){invariant(!Object.hasOwn(inventory??{},"migrationRuntimeSchemaVersion"),"cannot project an already-packaged migration runtime inventory"),invariant(renameLedger&&typeof renameLedger=="object","createMigrationRuntimeInventory needs the rename ledger"),buildMigrationContract(inventory,{renameLedger});const targetTags=new Set(inventory.mappings.map(mapping=>mapping.targetTag).filter(Boolean));for(const profile of inventory.localMigrations)targetTags.add(profile.tag);return{schemaVersion:1,migrationRuntimeSchemaVersion:MIGRATION_RUNTIME_SCHEMA_VERSION,accessibilityProfiles:structuredClone(inventory.accessibilityProfiles),components:inventory.components.filter(component=>targetTags.has(component.tag)).map(component=>({tag:component.tag,registrationModule:component.registrationModule,rootIncluded:component.rootIncluded,optionalPeers:structuredClone(component.optionalPeers??[]),surface:{attributes:structuredClone(component.surface?.attributes??[])}})),localMigrations:structuredClone(inventory.localMigrations),upstreams:Object.fromEntries(ECOSYSTEMS.map(ecosystem=>{const upstream=inventory.upstreams[ecosystem];return[ecosystem,{packages:structuredClone(upstream.packages),components:upstream.components.map(component=>({tag:component.tag,tier:component.tier}))}]})),mappings:inventory.mappings.map(mapping=>({upstream:mapping.upstream,upstreamTag:mapping.upstreamTag,targetTag:mapping.targetTag,classification:mapping.classification,rationale:mapping.rationale,parity:structuredClone(mapping.parity),rewrites:structuredClone(mapping.rewrites)})),lyraRenames:projectRenameLedger(renameLedger,inventory)}}function readRenameLedger(file=renameLedgerPath){return JSON.parse(fs.readFileSync(file,"utf8"))}function buildMirrorMap(readmeText){const map=new Map,conflicts=[];let mode=null;const setMapping=(from,to)=>{map.has(from)&&map.get(from)!==to?conflicts.push(`${from}: already mapped to ${map.get(from)}, also saw ${to}`):map.set(from,to)};for(const rawLine of readmeText.split(`
|
|
3
3
|
`)){const line=rawLine.trim();if(/^\|\s*Component\s*\|\s*Mirrors\s*\|\s*Notes\s*\|$/.test(line)){mode="wa";continue}if(/^\|\s*Shoelace\s*\|\s*Lyra\s*\|\s*Migration note\s*\|$/.test(line)){mode="sl";continue}if(!line.startsWith("|")){mode=null;continue}if(!mode)continue;const cells=line.split("|").slice(1,-1).map(cell=>cell.trim());if(cells.length<2)continue;if(mode==="sl"){const source=[...cells[0].matchAll(/<sl-([a-z0-9-]+)>/g)].map(match=>match[1]),targets2=new Set([...cells[1].matchAll(/<lr-([a-z0-9-]+)>/g)].map(match=>match[1]));for(const suffix of source)targets2.has(suffix)&&setMapping(`sl-${suffix}`,`lr-${suffix}`);continue}const targets=[...cells[0].matchAll(/<lr-([a-z0-9-]+)>/g)].map(match=>match[1]),sources=[...cells[1].matchAll(/`((?:wa|sl)-[a-z0-9-]+\*?)`/g)].map(match=>match[1]);if(!targets.length||!sources.length)continue;const consumed=new Set;for(const suffix of targets)for(const prefix of["wa","sl"]){const expected=`${prefix}-${suffix}`;if(sources.includes(expected))setMapping(expected,`lr-${suffix}`),consumed.add(expected);else{const wildcard=sources.find(source=>source.endsWith("*")&&expected.startsWith(source.slice(0,-1)));wildcard&&(setMapping(expected,`lr-${suffix}`),consumed.add(wildcard))}}if(targets.length===1)for(const source of sources)!consumed.has(source)&&!source.endsWith("*")&&setMapping(source,`lr-${targets[0]}`)}return{map,conflicts}}function warningCode(mapping){return mapping?mapping.classification==="warning-required"?"WARNING_REQUIRED":mapping.classification==="conceptual-only"?"CONCEPTUAL_MAPPING":"UNSUPPORTED_MAPPING":"UNKNOWN_UPSTREAM_TAG"}function ecosystemForTag(tag){return tag.startsWith("wa-")?"webawesome":"shoelace"}function lineStarts(text){const starts=[0];for(let index=0;index<text.length;index+=1)text[index]===`
|
|
4
4
|
`&&starts.push(index+1);return starts}function locationAt(starts,offset){let low=0,high=starts.length;for(;low+1<high;){const middle=Math.floor((low+high)/2);starts[middle]<=offset?low=middle:high=middle}return{line:low+1,column:offset-starts[low]+1}}function reportEntry({textStarts,file,offset,origin,upstreamTag,upstreamMember=null,action,target=null,warningCode:code=null,behaviorReviewFlags=null,message}){const location=locationAt(textStarts,offset),entry={file,line:location.line,column:location.column,origin,upstreamTag,upstreamMember,action,target,warningCode:code,message};return behaviorReviewFlags?.length&&(entry.behaviorReviewFlags=Object.freeze([...behaviorReviewFlags])),entry}function commentRanges(text){const ranges=[];let index=0;for(;index<text.length;){if(text.startsWith("<!--",index)){const end=text.indexOf("-->",index+4),finish=end<0?text.length:end+3;ranges.push([index,finish]),index=finish;continue}if(text.startsWith("/*",index)){const end=text.indexOf("*/",index+2),finish=end<0?text.length:end+2;ranges.push([index,finish]),index=finish;continue}if(text.startsWith("//",index)){const end=text.indexOf(`
|
|
5
5
|
`,index+2),finish=end<0?text.length:end;ranges.push([index,finish]),index=finish;continue}if(text[index]==='"'||text[index]==="'"||text[index]==="`"){const quote=text[index++];for(;index<text.length;)if(text[index]==="\\")index+=2;else if(text[index]===quote){index+=1;break}else index+=1;continue}index+=1}return ranges}function insideRanges(offset,ranges){return ranges.some(([start,end])=>offset>=start&&offset<end)}function quotedRangeContaining(text,offset){let index=0;for(;index<text.length;){const character=text[index];if(character!=='"'&&character!=="'"&&character!=="`"){index+=1;continue}const start=index,quote=character;for(index+=1;index<text.length;)if(text[index]==="\\")index+=2;else if(text[index]===quote){index+=1;break}else index+=1;if(offset>=start&&offset<index)return[start,index]}return null}function htmlTemplateStyleRanges(text){const ranges=[];for(const match of text.matchAll(/<style(?:\s[^>]*)?>(?<body>[\s\S]*?)<\/style\s*>/gi)){const quoted=quotedRangeContaining(text,match.index);if(!quoted||text[quoted[0]]!=="`"||!/\bhtml\s*$/.test(text.slice(0,quoted[0]))||insideTemplateExpression(text,quoted[0],match.index))continue;const bodyStart=match.index+match[0].indexOf(match.groups.body);ranges.push([bodyStart,bodyStart+match.groups.body.length])}return ranges}function insideTemplateExpression(text,templateStart,offset){let expressionDepth=0;for(let index=templateStart+1;index<offset;index+=1){if(text[index]==="\\"){index+=1;continue}if(expressionDepth===0){if(text.startsWith("${",index))expressionDepth=1,index+=1;else if(text[index]==="`")return!1;continue}if(text[index]==='"'||text[index]==="'"){const quote=text[index];for(index+=1;index<offset&&text[index]!==quote;)text[index]==="\\"&&(index+=1),index+=1;continue}if(text.startsWith("//",index)){const end=text.indexOf(`
|
|
6
|
-
`,index+2);index=end<0||end>=offset?offset:end;continue}if(text.startsWith("/*",index)){const end=text.indexOf("*/",index+2);index=end<0||end>=offset?offset:end+1;continue}text[index]==="{"?expressionDepth+=1:text[index]==="}"&&(expressionDepth=Math.max(0,expressionDepth-1))}return expressionDepth>0}function findTagEnd(text,start){let quote=null,braces=0;for(let index=start;index<text.length;index+=1){const character=text[index];if(quote){character==="\\"?index+=1:character===quote&&(quote=null);continue}if(character==='"'||character==="'"||character==="`"){quote=character;continue}if(character==="{")braces+=1;else if(character==="}")braces=Math.max(0,braces-1);else if(character===">"&&braces===0)return index}return-1}function scanMarkupTags(text,ignoredRanges){const tokens=[],regex=/<(?<closing>\/)?(?<tag>(?:wa|sl)-[a-z][a-z0-9-]*)(?=[\s/>])/g;for(const match of text.matchAll(regex)){if(insideRanges(match.index,ignoredRanges))continue;const closing=!!match.groups.closing,nameStart=match.index+1+(closing?1:0),end=findTagEnd(text,match.index+match[0].length);end<0||tokens.push({tag:match.groups.tag,start:match.index,nameStart,nameEnd:nameStart+match.groups.tag.length,end,closing,selfClosing:!closing&&/\/\s*>$/.test(text.slice(match.index,end+1))})}return tokens}function scanAllOpeningTags(text,ignoredRanges){const tokens=[],regex=/<(?<tag>[a-z][a-z0-9.-]*)(?=[\s/>])/gi;for(const match of text.matchAll(regex)){if(
|
|
6
|
+
`,index+2);index=end<0||end>=offset?offset:end;continue}if(text.startsWith("/*",index)){const end=text.indexOf("*/",index+2);index=end<0||end>=offset?offset:end+1;continue}text[index]==="{"?expressionDepth+=1:text[index]==="}"&&(expressionDepth=Math.max(0,expressionDepth-1))}return expressionDepth>0}function findTagEnd(text,start){let quote=null,braces=0;for(let index=start;index<text.length;index+=1){const character=text[index];if(quote){character==="\\"?index+=1:character===quote&&(quote=null);continue}if(character==='"'||character==="'"||character==="`"){quote=character;continue}if(character==="{")braces+=1;else if(character==="}")braces=Math.max(0,braces-1);else if(character===">"&&braces===0)return index}return-1}function scanMarkupTags(text,ignoredRanges){const tokens=[],regex=/<(?<closing>\/)?(?<tag>(?:wa|sl)-[a-z][a-z0-9-]*)(?=[\s/>])/g;for(const match of text.matchAll(regex)){if(insideRanges(match.index,ignoredRanges))continue;const closing=!!match.groups.closing,nameStart=match.index+1+(closing?1:0),end=findTagEnd(text,match.index+match[0].length);end<0||tokens.push({tag:match.groups.tag,start:match.index,nameStart,nameEnd:nameStart+match.groups.tag.length,end,closing,selfClosing:!closing&&/\/\s*>$/.test(text.slice(match.index,end+1))})}return tokens}function scanAllOpeningTags(text,ignoredRanges,inside=offset=>insideRanges(offset,ignoredRanges)){const tokens=[],regex=/<(?<tag>[a-z][a-z0-9.-]*)(?=[\s/>])/gi;for(const match of text.matchAll(regex)){if(inside(match.index))continue;const end=findTagEnd(text,match.index+match[0].length);if(end<0)continue;const nameStart=match.index+1;tokens.push({tag:match.groups.tag,start:match.index,nameStart,nameEnd:nameStart+match.groups.tag.length,end,closing:!1,selfClosing:/\/\s*>$/.test(text.slice(match.index,end+1))})}return tokens}const DOM_FACTORY_CONSTRUCTION="construction",DOM_FACTORY_LOOKUP="lookup",JS_IDENTIFIER="[$A-Z_a-z][$\\w]*";function normalizedMemberExpression(value){let expression=value.trim();for(;expression.startsWith("(")&&skipBalanced(expression,0,"(",")")===expression.length;)expression=expression.slice(1,-1).trim();return expression.replace(/\?\.\s*\[\s*(['"])([$A-Z_a-z][$\w]*)\1\s*\]/g,".$2").replace(/\[\s*(['"])([$A-Z_a-z][$\w]*)\1\s*\]/g,".$2").replace(/\s*(?:\?\.|\.)\s*/g,".")}function memberExpressionPattern(expression){const[root,...members]=expression.split("."),wrapped=pattern=>`(?:${pattern}\\s*!?|\\(\\s*${pattern}\\s+as\\s+[^;\\n]+?\\)\\s*!?)`;return members.reduce((pattern,member)=>{const escaped=regexEscape(member);return wrapped(`${pattern}(?:\\s*(?:\\.|\\?\\.)\\s*${escaped}|\\s*(?:\\?\\.)?\\s*\\[\\s*(?:'${escaped}'|"${escaped}"|\\x60${escaped}\\x60)\\s*\\])`)},wrapped(regexEscape(root)))}function canonicalFactoryExpression(value,callees){const normalized=normalizedMemberExpression(value);if(callees.has(normalized))return normalized;const source=value.trim();for(const candidate of callees.keys())if(new RegExp(`^(?:${memberExpressionPattern(candidate)})$`).test(source))return candidate;return normalized}function classifyFactoryExpression(value,bindings,documentReferences,registryReferences,bareCreateElementAvailable=!0){let expression=normalizedMemberExpression(value);const bound=expression.match(/^(?<target>.+)\.bind\s*\([^()]*\)$/s);if(bound&&(expression=normalizedMemberExpression(bound.groups.target)),bindings.has(expression))return bindings.get(expression);if(expression==="createElement"&&bareCreateElementAvailable)return DOM_FACTORY_CONSTRUCTION;for(const reference of documentReferences)if(expression===`${reference}.createElement`)return DOM_FACTORY_CONSTRUCTION;for(const reference of registryReferences)if(expression===`${reference}.get`||expression===`${reference}.whenDefined`)return DOM_FACTORY_LOOKUP;return null}function classifyFactoryInitializer(value,bindings,documentReferences,registryReferences,bareCreateElementAvailable=!0){const direct=classifyFactoryExpression(value,bindings,documentReferences,registryReferences,bareCreateElementAvailable);if(direct)return direct;const arrow=value.match(new RegExp(`^(?:async\\s+)?(?:\\(\\s*(?<parameter>${JS_IDENTIFIER})[^)]*\\)|(?<bare>${JS_IDENTIFIER}))\\s*=>\\s*(?<body>.+)$`,"s"));if(!arrow)return null;const parameter=arrow.groups.parameter??arrow.groups.bare,call=arrow.groups.body.trim().replace(/^\{\s*return\s+/,"").replace(/\s*\}$/,"").trim().match(new RegExp(`^(?<factory>${JS_IDENTIFIER}(?:\\s*\\.\\s*${JS_IDENTIFIER})*)\\s*\\(\\s*${regexEscape(parameter)}\\s*\\)$`));return call?classifyFactoryExpression(call.groups.factory,bindings,documentReferences,registryReferences,bareCreateElementAvailable):null}function parseDestructuredBindings(source){const bindings=[];for(const rawEntry of source.split(",")){const entry=rawEntry.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/[^\n\r]*/g,"").trim();if(!entry||entry.startsWith("..."))continue;const match=entry.match(new RegExp(`^(?<property>${JS_IDENTIFIER})(?:\\s*:\\s*(?<local>${JS_IDENTIFIER}))?(?:\\s*=.*)?$`));match&&bindings.push({property:match.groups.property,local:match.groups.local??match.groups.property})}return bindings}function codeBraceRanges(text){const stack=[],ranges=[];for(let index=0;index<text.length;index+=1){if(text.startsWith("//",index)){const end=text.indexOf(`
|
|
7
7
|
`,index+2);index=end<0?text.length:end;continue}if(text.startsWith("/*",index)){const end=text.indexOf("*/",index+2);index=end<0?text.length:end+1;continue}const character=text[index];if(character==='"'||character==="'"||character==="`"){const quote=character;for(index+=1;index<text.length;)if(text[index]==="\\")index+=2;else{if(text[index]===quote)break;index+=1}continue}character==="{"?stack.push(index):character==="}"&&stack.length>0&&ranges.push([stack.pop(),index+1])}return ranges}function identifierAppears(source,name){return new RegExp(`(?<![$\\w])${regexEscape(name)}(?![$\\w])`).test(source)}function enclosingBraceRange(braceRanges,offset,textLength){let enclosing=null;for(const range of braceRanges)range[0]>=offset||range[1]<=offset||(!enclosing||range[1]-range[0]<enclosing[1]-enclosing[0])&&(enclosing=range);return enclosing??[0,textLength]}function topLevelIndex(source,wanted){const pairs={"(":")","[":"]","{":"}"},stack=[];let quote=null;for(let index=0;index<source.length;index+=1){const character=source[index];if(quote){character==="\\"?index+=1:character===quote&&(quote=null);continue}if(character==='"'||character==="'"||character==="`"){quote=character;continue}if(Object.hasOwn(pairs,character))stack.push(pairs[character]);else if(stack.at(-1)===character)stack.pop();else if(stack.length===0&&character===wanted)return index}return-1}function parameterListBindsName(parameters,name){let rest=parameters;for(;rest;){const comma=topLevelIndex(rest,","),parameter=(comma<0?rest:rest.slice(0,comma)).trim(),equals=topLevelIndex(parameter,"="),binding=(equals<0?parameter:parameter.slice(0,equals)).trim(),restBinding=binding.replace(/^\.\.\.\s*/,"");if(new RegExp(`^(?:(?:public|private|protected|readonly|override)\\s+)*(?:\\.\\.\\.\\s*)?${regexEscape(name)}(?=\\s*(?:[?:]|$))`).test(binding)||(restBinding.startsWith("{")||restBinding.startsWith("["))&&identifierAppears(restBinding,name))return!0;if(comma<0)break;rest=rest.slice(comma+1)}return!1}function nextCodeIndex(text,start){let index=start;for(;index<text.length;){if(/\s/.test(text[index])){index+=1;continue}if(text.startsWith("//",index)){const end=text.indexOf(`
|
|
8
8
|
`,index+2);index=end<0?text.length:end+1;continue}if(text.startsWith("/*",index)){const end=text.indexOf("*/",index+2);index=end<0?text.length:end+2;continue}break}return index}function matchingTypeParameterEnd(text,start){let depth=0,quote=null;for(let index=start;index<text.length;index+=1){const character=text[index];if(quote){character==="\\"?index+=1:character===quote&&(quote=null);continue}if(text.startsWith("//",index)){const end=text.indexOf(`
|
|
9
9
|
`,index+2);index=end<0?text.length:end;continue}if(text.startsWith("/*",index)){const end=text.indexOf("*/",index+2);index=end<0?text.length:end+1;continue}if(character==='"'||character==="'"||character==="`"){quote=character;continue}if(character==="<")depth+=1;else if(character===">"&&text[index-1]!=="="){if(depth-=1,depth===0)return index;if(depth<0)return-1}}return-1}function stripTrailingTypeParameters(text){const value=text.trimEnd();if(!value.endsWith(">"))return value;for(let index=0;index<value.length;index+=1)if(!(value[index]!=="<"||quotedRangeContaining(value,index))&&matchingTypeParameterEnd(value,index)===value.length-1)return value.slice(0,index).trimEnd();return value}function scanCallableScopes(text,ignoredRanges,braceRanges){const scopes=[],remember=(parameters,body,parameterRange)=>{if(!body)return;const key=`${body[0]}:${body[1]}:${parameters}`;scopes.some(scope=>scope.key===key)||scopes.push({key,parameters,parameterRange,body})};for(let open=text.indexOf("(");open>=0;open=text.indexOf("(",open+1)){if(insideRanges(open,ignoredRanges)||quotedRangeContaining(text,open))continue;const closeAfter=skipBalanced(text,open,"(",")");if(closeAfter<=open+1||closeAfter>text.length)continue;const close=closeAfter-1,prefix=text.slice(Math.max(0,open-200),open),callablePrefix=stripTrailingTypeParameters(prefix),functionLike=/\bfunction(?:\s*\*)?(?:\s+[$A-Z_a-z][$\w]*)?\s*$/s.test(callablePrefix),methodMatch=callablePrefix.match(/(?:^|[;{}\n])\s*(?:(?:public|private|protected|static|abstract|async|override|get|set)\s+)*(?:\*\s*)?(?<name>[$A-Z_a-z][$\w]*|constructor|\[['"][^'"\n]+['"]\])\s*$/s),methodLike=!!(methodMatch&&!new Set(["catch","for","if","switch","while","with"]).has(methodMatch.groups.name)),arrow=text.slice(closeAfter,Math.min(text.length,closeAfter+500)).match(/^\s*(?::[\s\S]*?)?=>/);let body=null;if(arrow){const bodyStart=nextCodeIndex(text,closeAfter+arrow[0].length);body=text[bodyStart]==="{"?braceRanges.find(([start])=>start===bodyStart)??null:[bodyStart,simpleStatementEnd(text,bodyStart)]}else if(functionLike||methodLike){const bodyStart=nextCodeIndex(text,closeAfter),openingBrace=text[bodyStart]==="{"?bodyStart:text.indexOf("{",bodyStart),boundary=text.slice(bodyStart,openingBrace<0?text.length:openingBrace);openingBrace>=0&&!/[;=]/.test(boundary)&&(body=braceRanges.find(([start])=>start===openingBrace)??null)}remember(text.slice(open+1,close),body,[open+1,close]),open=close}const bareArrow=new RegExp(`(?<![$\\w])(?<parameter>${JS_IDENTIFIER})\\s*=>`,"g");for(const match of text.matchAll(bareArrow)){if(insideRanges(match.index,ignoredRanges)||quotedRangeContaining(text,match.index))continue;const bodyStart=nextCodeIndex(text,match.index+match[0].length),body=text[bodyStart]==="{"?braceRanges.find(([start])=>start===bodyStart)??null:[bodyStart,simpleStatementEnd(text,bodyStart)];remember(match.groups.parameter,body,[match.index,match.index+match.groups.parameter.length])}return scopes}function enclosingFunctionRange(callableScopes,offset,textLength){let enclosing=null;for(const{body}of callableScopes)body[0]>=offset||body[1]<=offset||(!enclosing||body[1]-body[0]<enclosing[1]-enclosing[0])&&(enclosing=body);return enclosing??[0,textLength]}function sameRange(left,right){return!!(left&&right&&left[0]===right[0]&&left[1]===right[1])}function identifierShadowRanges(text,name,ignoredRanges,braceRanges,callableScopes){const ranges=[],escaped=regexEscape(name),addBodyRange=(match,bodyStart)=>{if(insideRanges(match.index,ignoredRanges)||quotedRangeContaining(text,match.index))return;const body=braceRanges.find(([start])=>start===bodyStart);body&&ranges.push(body)},declarationPattern=new RegExp(`\\b(?<kind>const|let|var|function|class)\\s+${escaped}(?![$\\w])`,"g");for(const match of text.matchAll(declarationPattern))insideRanges(match.index,ignoredRanges)||quotedRangeContaining(text,match.index)||ranges.push(match.groups.kind==="var"||match.groups.kind==="function"?enclosingFunctionRange(callableScopes,match.index,text.length):enclosingBraceRange(braceRanges,match.index,text.length));const destructuringPattern=/\b(?<kind>const|let|var)\s*\{(?<bindings>[^{}\n]+)\}/g;for(const match of text.matchAll(destructuringPattern))insideRanges(match.index,ignoredRanges)||quotedRangeContaining(text,match.index)||parseDestructuredBindings(match.groups.bindings).some(({local})=>local===name)&&ranges.push(match.groups.kind==="var"?enclosingFunctionRange(callableScopes,match.index,text.length):enclosingBraceRange(braceRanges,match.index,text.length));const importPattern=/\bimport\s+(?<clause>[^'";\n]+?)\s+from\s*['"]/g;for(const match of text.matchAll(importPattern)){if(insideRanges(match.index,ignoredRanges)||quotedRangeContaining(text,match.index))continue;const clause=match.groups.clause.trim().replace(/^type\s+/,""),locals=[],defaultBinding=clause.match(new RegExp(`^(?<local>${JS_IDENTIFIER})(?:\\s*,|$)`));defaultBinding&&locals.push(defaultBinding.groups.local);const namespaceBinding=clause.match(new RegExp(`\\*\\s+as\\s+(?<local>${JS_IDENTIFIER})`));namespaceBinding&&locals.push(namespaceBinding.groups.local);const namedBindings=clause.match(/\{(?<bindings>[\s\S]*?)\}/)?.groups.bindings;namedBindings&&locals.push(...parseNamedModuleBindings(namedBindings).map(({local})=>local)),locals.includes(name)&&ranges.push([0,text.length])}for(const callable of callableScopes)parameterListBindsName(callable.parameters,name)&&ranges.push(callable.parameterRange,callable.body);const catchPattern=/\bcatch\s*\(/g;for(const match of text.matchAll(catchPattern)){if(insideRanges(match.index,ignoredRanges)||quotedRangeContaining(text,match.index))continue;const open=match.index+match[0].lastIndexOf("("),closeAfter=skipBalanced(text,open,"(",")");if(closeAfter<=open+1||closeAfter>text.length)continue;const binding=text.slice(open+1,closeAfter-1);if(!parameterListBindsName(binding,name))continue;const bodyStart=nextCodeIndex(text,closeAfter);text[bodyStart]==="{"&&addBodyRange(match,bodyStart)}return ranges}function identifierIsShadowedAt(shadowRanges,name,offset){return(shadowRanges.get(name)??[]).some(([start,end])=>offset>=start&&offset<end)}function innermostShadowRange(shadowRanges,name,offset){let innermost=null;for(const range of shadowRanges.get(name)??[])offset<range[0]||offset>=range[1]||(!innermost||range[1]-range[0]<innermost[1]-innermost[0])&&(innermost=range);return innermost}function visibleReferencesAt(references,referenceScopes,shadowRanges,offset){return new Set([...references].filter(reference=>{const root=reference.split(".")[0],shadow=innermostShadowRange(shadowRanges,root,offset);return(referenceScopes.get(reference)??[]).some(record=>offset>=record.scope[0]&&offset<record.scope[1]&&(record.global?!shadow:sameRange(record.scope,shadow)))}))}function declarationScope(kind,offset,braceRanges,callableScopes,textLength){return kind==="var"?enclosingFunctionRange(callableScopes,offset,textLength):enclosingBraceRange(braceRanges,offset,textLength)}function addScopedRecord(table,name,record){const records=table.get(name)??[];return records.some(candidate=>candidate.kind===record.kind&&candidate.declarationIndex===record.declarationIndex&&sameRange(candidate.scope,record.scope))?!1:(records.push(record),table.set(name,records),!0)}function visibleBindingRecord(factoryAnalysis,name,offset){const shadow=innermostShadowRange(factoryAnalysis.shadowRanges,name.split(".")[0],offset);return(factoryAnalysis.bindingScopes.get(name)??[]).filter(record=>offset>=record.scope[0]&&offset<record.scope[1]&&(record.global?!shadow:sameRange(record.scope,shadow))).sort((left,right)=>left.scope[1]-left.scope[0]-(right.scope[1]-right.scope[0])||right.declarationIndex-left.declarationIndex)[0]??null}function visibleBindingsAt(bindings,bindingScopes,shadowRanges,offset){const analysis={bindingScopes,shadowRanges};return new Map([...bindings].filter(([name])=>visibleBindingRecord(analysis,name,offset)))}function scanLocalDomFactoryBindings(text,ignoredRanges,seededBindings=new Map){const bindings=new Map(seededBindings),bindingScopes=new Map,documentReferences=new Set(["document","window.document","globalThis.document"]),registryReferences=new Set(["customElements","window.customElements","globalThis.customElements"]),documentReferenceScopes=new Map,registryReferenceScopes=new Map,moduleScope=[0,text.length];for(const reference of documentReferences)addScopedRecord(documentReferenceScopes,reference,{scope:moduleScope,declarationIndex:-1,global:!0});for(const reference of registryReferences)addScopedRecord(registryReferenceScopes,reference,{scope:moduleScope,declarationIndex:-1,global:!0});const braceRanges=codeBraceRanges(text),callableScopes=scanCallableScopes(text,ignoredRanges,braceRanges),declarationPattern=new RegExp(`\\b(?<declarationKind>const|let|var)\\s+(?<name>${JS_IDENTIFIER})(?:\\s*:[^=;\\n]+)?\\s*=\\s*(?<rhs>[^;\\n]+)`,"g"),declarations=[...text.matchAll(declarationPattern)].filter(match=>!insideRanges(match.index,ignoredRanges)&&!quotedRangeContaining(text,match.index)),destructuringPattern=/\b(?<declarationKind>const|let|var)\s*\{(?<bindings>[^{}\n]+)\}\s*=\s*(?<source>[$A-Z_a-z][$\w]*(?:\s*\.\s*[$A-Z_a-z][$\w]*)*)/g,destructurings=[...text.matchAll(destructuringPattern)].filter(match=>!insideRanges(match.index,ignoredRanges)&&!quotedRangeContaining(text,match.index)),functionPattern=new RegExp(`\\bfunction\\s+(?<name>${JS_IDENTIFIER})\\s*\\(\\s*(?<parameter>${JS_IDENTIFIER})[^)]*\\)\\s*\\{\\s*return\\s+(?<factory>${JS_IDENTIFIER}(?:\\s*\\.\\s*${JS_IDENTIFIER})*)\\s*\\(\\s*\\k<parameter>\\s*\\)\\s*;?\\s*\\}`,"g"),functions=[...text.matchAll(functionPattern)].filter(match=>!insideRanges(match.index,ignoredRanges)&&!quotedRangeContaining(text,match.index)),candidateNames=new Set(["createElement","customElements","document","globalThis","querySelector","querySelectorAll","window",...[...seededBindings.keys()].map(name=>name.split(".")[0]),...declarations.map(match=>match.groups.name),...destructurings.flatMap(match=>parseDestructuredBindings(match.groups.bindings).map(({local})=>local)),...functions.map(match=>match.groups.name)]),shadowRanges=new Map([...candidateNames].map(name=>[name,identifierShadowRanges(text,name,ignoredRanges,braceRanges,callableScopes)]));for(const[name,kind]of seededBindings)addScopedRecord(bindingScopes,name,{kind,scope:moduleScope,declarationIndex:-1,declarationEnd:0,declarationKind:"import"});const bindingPasses=Math.max(2,declarations.length+destructurings.length+functions.length+1);for(let pass=0;pass<bindingPasses;pass+=1){let changed=!1;for(const declaration of declarations){const name=declaration.groups.name,rhs=normalizedMemberExpression(declaration.groups.rhs),scope=declarationScope(declaration.groups.declarationKind,declaration.index,braceRanges,callableScopes,text.length),visibleDocuments=visibleReferencesAt(documentReferences,documentReferenceScopes,shadowRanges,declaration.index),visibleRegistries=visibleReferencesAt(registryReferences,registryReferenceScopes,shadowRanges,declaration.index);visibleDocuments.has(rhs)&&(documentReferences.add(name),changed=addScopedRecord(documentReferenceScopes,name,{scope,declarationIndex:declaration.index})||changed),visibleRegistries.has(rhs)&&(registryReferences.add(name),changed=addScopedRecord(registryReferenceScopes,name,{scope,declarationIndex:declaration.index})||changed);const visibleBindings=visibleBindingsAt(bindings,bindingScopes,shadowRanges,declaration.index),kind=classifyFactoryInitializer(rhs,visibleBindings,visibleDocuments,visibleRegistries,!identifierIsShadowedAt(shadowRanges,"createElement",declaration.index));kind&&(bindings.set(name,kind),changed=addScopedRecord(bindingScopes,name,{kind,scope,declarationIndex:declaration.index,declarationEnd:declaration.index+declaration[0].length,declarationKind:declaration.groups.declarationKind})||changed)}for(const destructuring of destructurings){const source=normalizedMemberExpression(destructuring.groups.source),scope=declarationScope(destructuring.groups.declarationKind,destructuring.index,braceRanges,callableScopes,text.length),fromDocument=visibleReferencesAt(documentReferences,documentReferenceScopes,shadowRanges,destructuring.index).has(source),fromRegistry=visibleReferencesAt(registryReferences,registryReferenceScopes,shadowRanges,destructuring.index).has(source);if(!(!fromDocument&&!fromRegistry))for(const binding of parseDestructuredBindings(destructuring.groups.bindings)){const kind=fromDocument&&binding.property==="createElement"?DOM_FACTORY_CONSTRUCTION:fromRegistry&&(binding.property==="get"||binding.property==="whenDefined")?DOM_FACTORY_LOOKUP:null;kind&&(bindings.set(binding.local,kind),changed=addScopedRecord(bindingScopes,binding.local,{kind,scope,declarationIndex:destructuring.index,declarationEnd:destructuring.index+destructuring[0].length,declarationKind:destructuring.groups.declarationKind})||changed)}}for(const match of functions){const visibleDocuments=visibleReferencesAt(documentReferences,documentReferenceScopes,shadowRanges,match.index),visibleRegistries=visibleReferencesAt(registryReferences,registryReferenceScopes,shadowRanges,match.index),kind=classifyFactoryExpression(match.groups.factory,visibleBindingsAt(bindings,bindingScopes,shadowRanges,match.index),visibleDocuments,visibleRegistries,!identifierIsShadowedAt(shadowRanges,"createElement",match.index));if(kind){const scope=enclosingFunctionRange(callableScopes,match.index,text.length);bindings.set(match.groups.name,kind),changed=addScopedRecord(bindingScopes,match.groups.name,{kind,scope,declarationIndex:match.index,declarationEnd:match.index+match[0].length,declarationKind:"function"})||changed}}if(!changed)break}const moduleBindings=new Map;for(const[name,records]of bindingScopes){const record=records.find(({scope})=>sameRange(scope,moduleScope));record&&moduleBindings.set(name,record.kind)}return{bindings,bindingScopes,moduleBindings,documentReferences,documentReferenceScopes,registryReferences,registryReferenceScopes,shadowRanges}}function assignedConstructionAlias(text,callStart){const boundary=Math.max(text.lastIndexOf(";",callStart-1),text.lastIndexOf("{",callStart-1),text.lastIndexOf("}",callStart-1));return text.slice(boundary+1,callStart).match(new RegExp(`\\b(?:export\\s+)?(?:const|let|var)\\s+(?<alias>${JS_IDENTIFIER})(?:\\s*:[^=]+)?\\s*=\\s*$`,"s"))?.groups.alias??null}function bindingIsStableBefore(text,name,callStart,expectedKind,factoryAnalysis){const record=visibleBindingRecord(factoryAnalysis,name,callStart);if(!record||record.kind!==expectedKind||!new Set(["const","function","import"]).has(record.declarationKind)||record.declarationKind==="const"&&callStart<record.declarationEnd)return!1;const start=Math.max(record.declarationEnd??0,record.scope[0]),intervening=text.slice(start,callStart),pattern=new RegExp(`(?<![$\\w.])${regexEscape(name)}\\s*=(?!=|>)`,"g"),comments=commentRanges(intervening);return![...intervening.matchAll(pattern)].some(match=>!insideRanges(match.index,comments)&&!quotedRangeContaining(intervening,match.index)&&sameRange(record.scope,innermostShadowRange(factoryAnalysis.shadowRanges,name.split(".")[0],start+match.index)))}function scanFactoryTagReferences(text,ignoredRanges,factoryAnalysis){const callees=new Map(factoryAnalysis.bindings);callees.set("createElement",DOM_FACTORY_CONSTRUCTION);for(const reference of factoryAnalysis.documentReferences)callees.set(`${reference}.createElement`,DOM_FACTORY_CONSTRUCTION);for(const reference of factoryAnalysis.registryReferences)callees.set(`${reference}.get`,DOM_FACTORY_LOOKUP),callees.set(`${reference}.whenDefined`,DOM_FACTORY_LOOKUP);const alternatives=[...callees.keys()].sort((left,right)=>right.length-left.length).map(memberExpressionPattern).join("|");if(!alternatives)return[];const pattern=new RegExp(`(?<![$\\w./])(?<api>(?:\\(\\s*(?:${alternatives})\\s*\\)|(?:${alternatives})))(?![$\\w])(?:\\s*\\.\\s*bind\\s*\\([^()]*\\))?\\s*(?:<[^>\\n]+>)?\\s*(?:(?:\\?\\.)?\\s*\\(\\s*|\\.\\s*call\\s*\\(\\s*[^,()]+\\s*,\\s*)(?<quote>['"\\x60])(?<tag>(?:wa|sl)-[a-z][a-z0-9-]*)\\k<quote>`,"g"),references=[];for(const match of text.matchAll(pattern)){const relative=match[0].lastIndexOf(match.groups.tag),start=match.index+relative;if(insideRanges(match.index,ignoredRanges)||insideRanges(start,ignoredRanges))continue;const tagQuote=quotedRangeContaining(text,start),calleeQuote=quotedRangeContaining(text,match.index);if(tagQuote&&calleeQuote&&tagQuote[0]===calleeQuote[0]&&tagQuote[1]===calleeQuote[1]&&(text[tagQuote[0]]!=="`"||!insideTemplateExpression(text,tagQuote[0],match.index)))continue;const api=canonicalFactoryExpression(match.groups.api,callees);if(factoryAnalysis.bindings.has(api)){if(!bindingIsStableBefore(text,api,match.index,callees.get(api),factoryAnalysis))continue}else if(api==="createElement"){if(identifierIsShadowedAt(factoryAnalysis.shadowRanges,api,match.index))continue}else if(api.endsWith(".createElement")){const reference=api.slice(0,-14);if(!visibleReferencesAt(factoryAnalysis.documentReferences,factoryAnalysis.documentReferenceScopes,factoryAnalysis.shadowRanges,match.index).has(reference))continue}else if(api.endsWith(".get")||api.endsWith(".whenDefined")){const suffix=api.endsWith(".get")?".get":".whenDefined",reference=api.slice(0,-suffix.length);if(!visibleReferencesAt(factoryAnalysis.registryReferences,factoryAnalysis.registryReferenceScopes,factoryAnalysis.shadowRanges,match.index).has(reference))continue}const open=match.index+match[0].lastIndexOf("(",relative),callEnd=skipBalanced(text,open,"(",")");references.push({tag:match.groups.tag,start,end:start+match.groups.tag.length,callStart:match.index,callEnd:callEnd??match.index+match[0].length,resultAlias:assignedConstructionAlias(text,match.index),kind:callees.get(api)})}return references}function scanApiTagReferences(text,ignoredRanges,suppliedFactoryBindings=null){const factoryAnalysis=scanLocalDomFactoryBindings(text,ignoredRanges,suppliedFactoryBindings??new Map),references=scanFactoryTagReferences(text,ignoredRanges,factoryAnalysis),lookupPattern=/(?<![$\w.])(?<api>(?:(?:window|globalThis)\.)?document\.querySelector(?:All)?|querySelector(?:All)?)\s*(?:<[^>\n]+>)?\(\s*(?<quote>['"])(?<tag>(?:wa|sl)-[a-z][a-z0-9-]*)\k<quote>/g;for(const match of text.matchAll(lookupPattern)){if(insideRanges(match.index,ignoredRanges))continue;if(match.groups.api.includes("document.")){const reference=match.groups.api.replace(/\.querySelector(?:All)?$/,"");if(!visibleReferencesAt(factoryAnalysis.documentReferences,factoryAnalysis.documentReferenceScopes,factoryAnalysis.shadowRanges,match.index).has(reference))continue}else if(identifierIsShadowedAt(factoryAnalysis.shadowRanges,match.groups.api,match.index))continue;const relative=match[0].lastIndexOf(match.groups.tag),start=match.index+relative;references.push({tag:match.groups.tag,start,end:start+match.groups.tag.length,callStart:match.index,callEnd:match.index+match[0].length,resultAlias:null,kind:DOM_FACTORY_LOOKUP})}const unique=new Map;for(const reference of references)unique.set(`${reference.start}:${reference.end}:${reference.kind}`,reference);return[...unique.values()].sort((left,right)=>left.start-right.start||left.end-right.end)}function scanAliasedRewriteReviews(text,contract,ignoredRanges){const reviews=new Map,addUse=(mapping,tagStart,alias,offset,upstreamMember,target,message)=>{const record=reviews.get(mapping.upstreamTag)??{mapping,aliases:[],uses:[]};reviews.has(mapping.upstreamTag)||reviews.set(mapping.upstreamTag,record),record.aliases.some(entry=>entry.tagStart===tagStart)||record.aliases.push({tagStart,alias});const key=`${offset}:${upstreamMember}:${target}`;record.uses.some(entry=>entry.key===key)||record.uses.push({key,offset,alias,upstreamMember,target,message})};for(const mapping of contract.mappings.values()){if(!AUTO_CLASSIFICATIONS.has(mapping.classification))continue;const escapedTag=mapping.upstreamTag.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),declaration=new RegExp(`\\b(?:const|let|var)\\s+(?<alias>[$A-Z_a-z][$\\w]*)\\s*=\\s*(?:document\\.)?querySelector(?:<[^>\\n]+>)?\\(\\s*(['"])${escapedTag}\\2\\s*\\)`,"g");for(const match of text.matchAll(declaration)){if(insideRanges(match.index,ignoredRanges))continue;const alias=match.groups.alias,tagStart=match.index+match[0].lastIndexOf(mapping.upstreamTag),afterDeclaration=match.index+match[0].length,owner=`(?<![$\\w])${alias.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\s*(?:\\?\\.|!\\s*\\.|\\.)\\s*`;for(const[section,suffix]of[["methods","(?=\\s*\\()"],["properties","(?=\\s*(?:=|\\.|\\?|;|$))"]])for(const rule of mapping.rewrites[section]){const member=rule.from.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),regex=new RegExp(`${owner}(?<member>${member})${suffix}`,"g");regex.lastIndex=afterDeclaration;for(let use=regex.exec(text);use;use=regex.exec(text)){const offset=use.index+use[0].lastIndexOf(use.groups.member);insideRanges(offset,ignoredRanges)||addUse(mapping,tagStart,alias,offset,rule.from,rule.to,`Aliased ${section} member ${alias}.${rule.from} must be migrated with its selector.`)}}for(const rule of mapping.rewrites.events){const member=rule.from.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),regex=new RegExp(`${owner}(?:add|remove)EventListener\\(\\s*(['"])(?<member>${member})\\1`,"g");regex.lastIndex=afterDeclaration;for(let use=regex.exec(text);use;use=regex.exec(text)){const offset=use.index+use[0].lastIndexOf(use.groups.member);insideRanges(offset,ignoredRanges)||addUse(mapping,tagStart,alias,offset,rule.from,rule.to,`Aliased event ${alias}.${rule.from} must be migrated with its selector.`)}}for(const rule of mapping.rewrites.attributes){const member=rule.from.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),regex=new RegExp(`${owner}(?:get|set|has|toggle|remove)Attribute\\(\\s*(['"])(?<member>${member})\\1`,"g");regex.lastIndex=afterDeclaration;for(let use=regex.exec(text);use;use=regex.exec(text)){const offset=use.index+use[0].lastIndexOf(use.groups.member);insideRanges(offset,ignoredRanges)||addUse(mapping,tagStart,alias,offset,rule.from,rule.to,`Aliased attribute ${alias}.${rule.from} must be migrated with its selector.`)}}}}return reviews}function skipBalanced(text,start,open,close){let depth=0,quote=null;for(let index=start;index<text.length;index+=1){const character=text[index];if(quote){character==="\\"?index+=1:character===quote&&(quote=null);continue}if(character==='"'||character==="'"||character==="`")quote=character;else if(character===open)depth+=1;else if(character===close&&--depth===0)return index+1}return text.length}function constructionStatementEnd(text,callEnd){const lineEnd=text.indexOf(`
|
|
@@ -12,23 +12,43 @@ import fs from"node:fs";import path from"node:path";import{fileURLToPath}from"no
|
|
|
12
12
|
`,index+2);index=end<0?text.length:end+1;continue}if(text.startsWith("/*",index)){const end=text.indexOf("*/",index+2);index=end<0?text.length:end+2;continue}if(pattern.lastIndex=index,pattern.exec(text))return!0;index+=1}return!1}function simpleStatementEnd(text,start){const depths={"(":0,"[":0,"{":0},closes={")":"(","]":"[","}":"{"};let quote=null;for(let index=start;index<text.length;index+=1){const character=text[index];if(quote){character==="\\"?index+=1:character===quote&&(quote=null);continue}if(text.startsWith("//",index)){const end=text.indexOf(`
|
|
13
13
|
`,index+2);return end<0?text.length:end+1}if(text.startsWith("/*",index)){const end=text.indexOf("*/",index+2);index=end<0?text.length:end+1;continue}if(character==='"'||character==="'"||character==="`"){quote=character;continue}if(Object.hasOwn(depths,character))depths[character]+=1;else if(Object.hasOwn(closes,character)){const opening=closes[character];depths[opening]=Math.max(0,depths[opening]-1)}else if((character===";"||character===`
|
|
14
14
|
`)&&depths["("]===0&&depths["["]===0&&depths["{"]===0)return index+1}return text.length}function scanImmediateAliasWrite(text,start,alias){const escapedAlias=regexEscape(alias),source=text.slice(start),attribute=source.match(new RegExp(`^${escapedAlias}\\s*\\.\\s*setAttribute\\s*\\(\\s*(?<quote>['"])(?<member>[A-Za-z_:][-.A-Za-z0-9_:]*)\\k<quote>\\s*,`));if(attribute){const open=start+attribute[0].indexOf("("),callEnd=skipBalanced(text,open,"(",")");if(containsIdentifierCode(text.slice(open+1,callEnd-1),alias))return null;const next2=simpleStatementEnd(text,callEnd);return text.slice(callEnd,next2).replace(/[;\s]/g,"")?null:{member:attribute.groups.member,kind:"attribute",next:next2}}const dotProperty=source.match(new RegExp(`^${escapedAlias}\\s*\\.\\s*(?<member>${JS_IDENTIFIER})\\s*=(?!=|>)`)),bracketProperty=dotProperty?null:source.match(new RegExp(`^${escapedAlias}\\s*\\[\\s*(?<quote>['"])(?<member>[^'"]+)\\k<quote>\\s*\\]\\s*=(?!=|>)`)),property=dotProperty??bracketProperty;if(!property)return null;const next=simpleStatementEnd(text,start+property[0].length),rightHandSide=text.slice(start+property[0].length,next);return containsIdentifierCode(rightHandSide,alias)?null:{member:property.groups.member,kind:"property",next}}function defaultWriteMatches(rule,write){const variants=new Set([rule.member,kebabCase(rule.member),camelCase(rule.member)]);if(write.kind==="attribute"){const name=write.member.toLowerCase();return[...variants].some(variant=>variant.toLowerCase()===name)}return variants.has(write.member)}function assessImperativeDefaults(text,reference,defaults){if(!reference.resultAlias)return{safe:!1,missing:defaults};let cursor=constructionStatementEnd(text,reference.callEnd);if(cursor===null)return{safe:!1,missing:defaults};const missing=new Set(defaults);for(let writes=0;writes<64&&missing.size>0;writes+=1){cursor=skipMigrationTrivia(text,cursor);const write=scanImmediateAliasWrite(text,cursor,reference.resultAlias);if(!write)break;for(const rule of missing)defaultWriteMatches(rule,write)&&missing.delete(rule);cursor=write.next}return{safe:missing.size===0,missing:[...missing]}}function parseTagAttributes(text,token){const records=[];let index=token.nameEnd;for(;index<token.end;){for(;/\s/.test(text[index]??"");)index+=1;if(index>=token.end||text[index]==="/")break;if(text[index]==="{"){index=skipBalanced(text,index,"{","}");continue}const nameStart=index;for(;index<token.end&&!/[\s=/>]/.test(text[index]);)index+=1;const nameEnd=index;if(nameEnd===nameStart){index+=1;continue}for(;/\s/.test(text[index]??"");)index+=1;let valueKind="boolean",valueStart=null,valueEnd=null;if(text[index]==="="){for(index+=1;/\s/.test(text[index]??"");)index+=1;if(text[index]==='"'||text[index]==="'"){const quote=text[index++];for(valueKind="literal",valueStart=index;index<token.end&&text[index]!==quote;)text[index]==="\\"&&(index+=1),index+=1;valueEnd=index,text[index]===quote&&(index+=1)}else if(text[index]==="{")valueKind="expression",valueStart=index,index=skipBalanced(text,index,"{","}"),valueEnd=index;else{for(valueKind="literal",valueStart=index;index<token.end&&!/[\s>]/.test(text[index]);)index+=1;valueEnd=index}}records.push({rawName:text.slice(nameStart,nameEnd),nameStart,nameEnd,valueKind,valueStart,valueEnd,value:valueStart===null?null:text.slice(valueStart,valueEnd)})}return records}function kebabCase(name){return name.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replaceAll("_","-").toLowerCase()}function camelCase(name){return name.replace(/-([a-z0-9])/g,(_match,letter)=>letter.toUpperCase())}function pascalCase(name){const camel=camelCase(name);return camel[0]?.toUpperCase()+camel.slice(1)}function memberStyle(source,target){return source===source.toLowerCase()?kebabCase(target):camelCase(target)}function namedRule(rawName,mapping){const candidates=[],addCandidate=(section,prefix,name,suffix="")=>candidates.push({section,prefix,name,suffix});rawName.startsWith("v-on:")?addCandidate("events","v-on:",rawName.slice(5)):rawName.startsWith("on:")?addCandidate("events","on:",rawName.slice(3)):rawName.startsWith("@")?addCandidate("events","@",rawName.slice(1)):/^on[A-Z]/.test(rawName)?addCandidate("events","on",rawName.slice(2),"jsx-event"):rawName.startsWith("v-bind:")?addCandidate("properties","v-bind:",rawName.slice(7)):rawName.startsWith("bind:")?addCandidate("properties","bind:",rawName.slice(5)):rawName.startsWith(":")?addCandidate("properties",":",rawName.slice(1)):rawName.startsWith(".")?addCandidate("properties",".",rawName.slice(1)):rawName.startsWith("?")?addCandidate("attributes","?",rawName.slice(1)):(addCandidate("attributes","",rawName),addCandidate("properties","",rawName));for(const candidate of candidates)for(const rule of mapping.rewrites[candidate.section]){if(!(candidate.suffix==="jsx-event"?candidate.name===pascalCase(rule.from):new Set([rule.from,kebabCase(rule.from),camelCase(rule.from)]).has(candidate.name)))continue;const targetName=candidate.suffix==="jsx-event"?pascalCase(rule.to):memberStyle(candidate.name,rule.to);return{section:candidate.section,from:rule.from,to:rule.to,replacement:`${candidate.prefix}${targetName}`}}return null}function isDynamicAttribute(attribute){return attribute.valueKind==="expression"||/^(?:[.?:]|v-bind:|bind:)/.test(attribute.rawName)}function scanDynamicDefaultReviews(text,markupTokens,contract){const reviews=new Map;for(const token of markupTokens){if(token.closing)continue;const mapping=contract.mappings.get(token.tag);if(!AUTO_CLASSIFICATIONS.has(mapping?.classification))continue;const attributes=parseTagAttributes(text,token);for(const rule of mapping.rewrites.defaults){if(rule.action!=="replace-value")continue;const variants=new Set([rule.member,kebabCase(rule.member),camelCase(rule.member)]),attribute=attributes.find(entry=>variants.has(entry.rawName.replace(/^[.?:]/,"")));if(!attribute||attribute.valueKind==="boolean"||!isDynamicAttribute(attribute))continue;const record=reviews.get(mapping.upstreamTag)??{mapping,uses:[]};reviews.has(mapping.upstreamTag)||reviews.set(mapping.upstreamTag,record),record.uses.push({offset:attribute.valueStart,upstreamMember:rule.member,target:String(rule.to),message:`The ${rule.member} value is dynamic; migrate ${String(rule.from)} to ${String(rule.to)} at its source before changing this component mapping.`})}}return reviews}const VOID_HTML_TAGS=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),RANDOM_CONTENT_BEHAVIOR_MESSAGES=Object.freeze({"host-layout":"Web Awesome uses display: contents while Lyra uses a block host with a shadow base; review inline placement and host-authored layout CSS.","multi-item-layout":"Lyra owns the simultaneous-item flex/wrap/gap context inside its shadow base instead of leaving it on the host.","unique-retry-bound":"Web Awesome promises non-repeating unique selection while Lyra uses a bounded retry and can eventually repeat.","forwarded-slot-candidates":"Lyra flattens a direct forwarding slot into projected candidates while Web Awesome treats only its direct element child as the candidate.","autoplay-semantics":"Lyra stops autoplay for reduced motion, keeps timer ticks silent, does not pause on hover, and exposes a localized paused control/state/event absent upstream."});function pairedMarkupTokens(tokens){const stack=[],pairs=new Map;for(const token of tokens){if(!token.closing&&!token.selfClosing){stack.push(token);continue}if(token.closing)for(let index=stack.length-1;index>=0;index-=1){if(stack[index].tag!==token.tag)continue;const[opening]=stack.splice(index,1);pairs.set(opening.start,token);break}}return pairs}function scanDirectElementChildren(text,start,end,ignoredRanges){const directTags=[],stack=[];let cursor=start,dynamicDirectContent=!1;const tagPattern=/<(?<closing>\/)?(?<tag>[a-z][a-z0-9.:-]*)(?=[\s/>])/gi;tagPattern.lastIndex=start;const noteGap=(gapStart,gapEnd)=>{stack.length===0&&/\$\{|\{\{|<%/.test(text.slice(gapStart,gapEnd))&&(dynamicDirectContent=!0)};for(let match=tagPattern.exec(text);match&&match.index<end;match=tagPattern.exec(text)){if(insideRanges(match.index,ignoredRanges))continue;noteGap(cursor,match.index);const tokenEnd=findTagEnd(text,match.index+match[0].length);if(tokenEnd<0||tokenEnd>=end)break;const tagName=match.groups.tag.toLowerCase();if(match.groups.closing){for(let index=stack.length-1;index>=0;index-=1)if(stack[index]===tagName){stack.splice(index,1);break}}else stack.length===0&&directTags.push(tagName),!/\/\s*>$/.test(text.slice(match.index,tokenEnd+1))&&!VOID_HTML_TAGS.has(tagName)&&stack.push(tagName);cursor=tokenEnd+1,tagPattern.lastIndex=cursor}return noteGap(cursor,end),{directTags,dynamicDirectContent}}function normalizedAttributeName(attribute){return attribute.rawName.replace(/^(?:v-bind:|bind:|[.?:])/,"").toLowerCase()}function scanConditionalBehaviorReviews(text,markupTokens,ignoredRanges,contract){const reviews=new Map,pairs=pairedMarkupTokens(markupTokens);for(const opening of markupTokens){if(opening.closing||opening.tag!=="wa-random-content")continue;const mapping=contract.mappings.get(opening.tag);if(!AUTO_CLASSIFICATIONS.has(mapping?.classification)||!mapping.parity.behaviorReviewFlags.length)continue;const attributes=parseTagAttributes(text,opening),attribute=name=>attributes.find(candidate=>normalizedAttributeName(candidate)===name),closing=pairs.get(opening.start),children=closing?scanDirectElementChildren(text,opening.end+1,closing.start,ignoredRanges):{directTags:[],dynamicDirectContent:!opening.selfClosing},flags=[];(children.directTags.length>0||children.dynamicDirectContent)&&flags.push("host-layout");const items=attribute("items"),dynamicItems=items?isDynamicAttribute(items):!1,itemCount=!items||items.value===null?1:Number(items.value);(dynamicItems||Number.isFinite(itemCount)&&itemCount>1)&&flags.push("multi-item-layout");const mode=attribute("mode"),dynamicMode=mode?isDynamicAttribute(mode):!1,effectiveItems=Number.isFinite(itemCount)?Math.max(1,Math.trunc(itemCount)):1;(!mode||dynamicMode||mode.value==="unique")&&(dynamicMode||children.dynamicDirectContent||children.directTags.length>effectiveItems)&&flags.push("unique-retry-bound"),(children.directTags.includes("slot")||children.dynamicDirectContent)&&flags.push("forwarded-slot-candidates"),attribute("autoplay")&&flags.push("autoplay-semantics");const uniqueFlags=flags.filter((flag,index)=>mapping.parity.behaviorReviewFlags.includes(flag)&&flags.indexOf(flag)===index);uniqueFlags.length&&reviews.set(opening.start,{flags:uniqueFlags,message:`Review ${mapping.upstreamTag} before relying on the migrated behavior: `+uniqueFlags.map(flag=>`${flag}: ${RANDOM_CONTENT_BEHAVIOR_MESSAGES[flag]}`).join(" ")})}return reviews}function memberAction(section){return`rewrite-${{attributes:"attribute",properties:"property",events:"event",slots:"slot",parts:"part",cssProperties:"css-property",methods:"method"}[section]}`}function targetImport(component){return`@aceshooting/lyra-ui/${component.registrationModule.replace(/^src\//,"").replace(/\.ts$/,".js")}`}function registrationClosure(contract,upstreamTags){const targets=new Map;for(const upstreamTag of upstreamTags??[]){const mapping=contract.mappings.get(upstreamTag);!mapping||!AUTO_CLASSIFICATIONS.has(mapping.classification)||!mapping.target||targets.set(mapping.targetTag,mapping.target)}const rootIncluded=[...targets.values()].some(component=>component.rootIncluded!==!1),granular=[...targets.values()].filter(component=>component.rootIncluded===!1).map(targetImport).sort();return[...rootIncluded?["@aceshooting/lyra-ui/all.js"]:[],...granular]}function deepImportTag(specifier,ecosystem){const match=specifier.match(/\/components\/([a-z0-9-]+)\/(?:\1(?:\.component)?|index)\.(?:js|mjs)$/);return match?`${ecosystem==="webawesome"?"wa":"sl"}-${match[1]}`:null}function packageProvidesMapping(imported,mapping){return!!(mapping&&(!mapping.source?.tier||imported.tiers.has(mapping.source.tier)))}function stripTrailingModuleTrivia(text){let value=text;for(;;){const next=value.replace(/\s+$/,"").replace(/\/\*[\s\S]*?\*\/$/,"").replace(/\/\/[^\n\r]*$/,"");if(next===value)return value;value=next}}function moduleSpecifierContext(text,quoteStart){const prefix=stripTrailingModuleTrivia(text.slice(0,quoteStart));return/\bimport\s*\($/.test(prefix)?{sideEffect:!1,kind:"dynamic-import"}:/\brequire\s*\($/.test(prefix)?{sideEffect:!1,kind:"require"}:/\bfrom$/.test(prefix)?{sideEffect:!1,kind:"binding"}:/\bimport$/.test(prefix)?{sideEffect:!0,kind:"import"}:null}function regexEscape(value){return value.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function scanImports(text,ignoredRanges,contract){const packageAlternatives=[...contract.packageIdentities.keys()].sort((left,right)=>right.length-left.length).map(regexEscape).join("|"),packagePattern=new RegExp(`(['"])(?<specifier>${packageAlternatives})(?<subpath>\\/[^'"\\s]*)?\\1`,"g"),imports=[];for(const match of text.matchAll(packagePattern)){if(insideRanges(match.index,ignoredRanges))continue;const quoteStart=match.index,context=moduleSpecifierContext(text,quoteStart);if(!context)continue;const specifier=`${match.groups.specifier}${match.groups.subpath??""}`,identity=contract.packageIdentities.get(match.groups.specifier);invariant(identity,`unrecognized package identity ${match.groups.specifier}`);const closingQuoteEnd=quoteStart+match[0].length,lineBreak=text.indexOf(`
|
|
15
|
-
`,closingQuoteEnd),lineEnd=lineBreak===-1?text.length:lineBreak,semicolon=text.indexOf(";",closingQuoteEnd),statementEnd=semicolon!==-1&&semicolon<lineEnd?semicolon+1:lineEnd;imports.push({...context,ecosystem:identity.ecosystem,tiers:identity.tiers,packageName:match.groups.specifier,specifier,subpath:match.groups.subpath??"",quote:match[1],start:quoteStart+1,end:quoteStart+1+specifier.length,statementEnd})}return imports}function parseNamedModuleBindings(source){const bindings=[],withoutComments=source.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/[^\n\r]*/g,"");for(const rawEntry of withoutComments.split(",")){const entry=rawEntry.trim().replace(/^type\s+/,"");if(!entry)continue;const match=entry.match(new RegExp(`^(?<imported>${JS_IDENTIFIER})(?:\\s+as\\s+(?<local>${JS_IDENTIFIER}))?$`));match&&bindings.push({imported:match.groups.imported,local:match.groups.local??match.groups.imported})}return bindings}function scanModuleFactoryLinks(text,ignoredRanges){const imports=[],localExports=[],reexports=[],starExports=[],addMatches=(pattern,visit)=>{for(const match of text.matchAll(pattern))!insideRanges(match.index,ignoredRanges)&&!quotedRangeContaining(text,match.index)&&visit(match)};return addMatches(/\bimport\s+(?!type\b)\{(?<bindings>[\s\S]*?)\}\s*from\s*(?<quote>['"])(?<source>[^'"]+)\k<quote>/g,match=>{for(const binding of parseNamedModuleBindings(match.groups.bindings))imports.push({source:match.groups.source,imported:binding.imported,local:binding.local})}),addMatches(new RegExp(`\\bimport\\s+(?!type\\b)(?<local>${JS_IDENTIFIER})(?:\\s*,\\s*(?:\\{[\\s\\S]*?\\}|\\*\\s+as\\s+${JS_IDENTIFIER}))?\\s+from\\s*(?<quote>['"])(?<source>[^'"]+)\\k<quote>`,"g"),match=>imports.push({source:match.groups.source,imported:"default",local:match.groups.local})),addMatches(new RegExp(`\\bimport\\s+\\*\\s+as\\s+(?<local>${JS_IDENTIFIER})\\s+from\\s*(?<quote>['"])(?<source>[^'"]+)\\k<quote>`,"g"),match=>imports.push({source:match.groups.source,imported:"*",local:match.groups.local})),addMatches(/\bexport\s*\{(?<bindings>[\s\S]*?)\}\s*from\s*(?<quote>['"])(?<source>[^'"]+)\k<quote>/g,match=>{for(const binding of parseNamedModuleBindings(match.groups.bindings))reexports.push({source:match.groups.source,imported:binding.imported,exported:binding.local})}),addMatches(/\bexport\s*\*\s*from\s*(?<quote>['"])(?<source>[^'"]+)\k<quote>/g,match=>starExports.push(match.groups.source)),addMatches(/\bexport\s*\{(?<bindings>[\s\S]*?)\}(?!\s*from\b)/g,match=>{for(const binding of parseNamedModuleBindings(match.groups.bindings))localExports.push({local:binding.imported,exported:binding.local})}),addMatches(new RegExp(`\\bexport\\s+(?:const|let|var|function)\\s+(?<name>${JS_IDENTIFIER})`,"g"),match=>localExports.push({local:match.groups.name,exported:match.groups.name})),addMatches(new RegExp(`\\bexport\\s+default\\s+(?<name>${JS_IDENTIFIER})(?![$\\w])`,"g"),match=>localExports.push({local:match.groups.name,exported:"default"})),{imports,localExports,reexports,starExports}}const MODULE_SOURCE_EXTENSIONS=[".ts",".tsx",".mts",".cts",".js",".jsx",".mjs",".cjs"];function resolveScannedModule(fromFile,specifier,scannedFiles){if(!specifier.startsWith("."))return null;const base=path.resolve(path.dirname(fromFile),specifier),extension=path.extname(base),candidates=[base];if(extension){const stem=base.slice(0,-extension.length);for(const candidateExtension of MODULE_SOURCE_EXTENSIONS)candidates.push(`${stem}${candidateExtension}`)}else for(const candidateExtension of MODULE_SOURCE_EXTENSIONS)candidates.push(`${base}${candidateExtension}`);for(const candidateExtension of MODULE_SOURCE_EXTENSIONS)candidates.push(path.join(base,`index${candidateExtension}`));return candidates.find(candidate=>scannedFiles.has(candidate))??null}function factoryMapEqual(left,right){return left.size===right.size&&[...left].every(([name,kind])=>right.get(name)===kind)}function buildProjectDomFactoryBindings(originals){const modules=new Map;for(const[file,text]of originals){const resolvedFile=path.resolve(file),ignoredRanges=commentRanges(text);modules.set(resolvedFile,{text,ignoredRanges,links:scanModuleFactoryLinks(text,ignoredRanges)})}const scannedFiles=new Set(modules.keys()),exportedBindings=new Map([...scannedFiles].map(file=>[file,new Map])),importedBindings=new Map([...scannedFiles].map(file=>[file,new Map]));for(let pass=0;pass<Math.max(4,modules.size*4);pass+=1){let changed=!1;for(const[file,module]of modules){const seeds=new Map;for(const imported of module.links.imports){const target=resolveScannedModule(file,imported.source,scannedFiles);if(!target)continue;const targetExports=exportedBindings.get(target);if(imported.imported==="*")for(const[name,kind]of targetExports)seeds.set(`${imported.local}.${name}`,kind);else{const kind=targetExports.get(imported.imported);kind&&seeds.set(imported.local,kind)}}const analysis=scanLocalDomFactoryBindings(module.text,module.ignoredRanges,seeds);factoryMapEqual(importedBindings.get(file),seeds)||(importedBindings.set(file,seeds),changed=!0);const nextExports=new Map;for(const exported of module.links.localExports){const kind=analysis.moduleBindings.get(exported.local);kind&&nextExports.set(exported.exported,kind)}for(const reexported of module.links.reexports){const target=resolveScannedModule(file,reexported.source,scannedFiles),kind=target?exportedBindings.get(target).get(reexported.imported):null;kind&&nextExports.set(reexported.exported,kind)}for(const source of module.links.starExports){const target=resolveScannedModule(file,source,scannedFiles);if(target)for(const[name,kind]of exportedBindings.get(target))name!=="default"&&nextExports.set(name,kind)}factoryMapEqual(exportedBindings.get(file),nextExports)||(exportedBindings.set(file,nextExports),changed=!0)}if(!changed)break}return importedBindings}function finalizeEdits(original,edits){const unique=[],seen=new Set;for(const edit of edits){const key=`${edit.start}:${edit.end}:${edit.replacement}`;seen.has(key)||(seen.add(key),unique.push(edit))}unique.sort((left,right)=>left.start-right.start||left.end-right.end);for(let index=1;index<unique.length;index+=1)if(unique[index].start<unique[index-1].end)throw new Error(`Migration rewrite conflict at offsets ${unique[index-1].start} and ${unique[index].start}`);let content=original;for(const edit of[...unique].sort((left,right)=>right.start-left.start||right.end-left.end))content=content.slice(0,edit.start)+edit.replacement+content.slice(edit.end);return content}function mappingMessage(mapping){return mapping?mapping.rationale||`The ${mapping.classification} mapping requires manual review.`:"No pinned inventory mapping exists for this upstream tag."}function localMigrationKey(origin,tag){return`${origin}:${tag}`}function localAttributeName(rawName){return rawName.replace(/^(?:v-bind:|bind:|[.?:])/,"").toLowerCase()}function serializeLocalDefault(rule){if(rule.value===!0)return rule.member;const value=String(rule.value).replaceAll("&","&").replaceAll('"',""");return`${rule.member}="${value}"`}function scanLocalMigrationHazards(text,profiles,ignoredRanges,openingTokens){const uses=[],blocked=new Set,tags=new Set(profiles.keys()),apiPattern=/\b(?:(?:[$A-Z_a-z][$\w]*)\s*(?:\?\.|\.)\s*(?:createElement|querySelector(?:All)?)|(?:createElement|querySelector(?:All)?)|customElements\.(?:get|whenDefined))\s*(?:<[^>\n]+>)?\(\s*(['"])(?<tag>lr-[a-z][a-z0-9-]*)\1/g;for(const match of text.matchAll(apiPattern)){if(insideRanges(match.index,ignoredRanges)||!tags.has(match.groups.tag))continue;const offset=match.index+match[0].lastIndexOf(match.groups.tag),key=localMigrationKey(profiles.get(match.groups.tag).origin,match.groups.tag);blocked.add(key),uses.push({key,tag:match.groups.tag,offset,warningCode:"ALIASED_MEMBER_REVIEW",message:`${match.groups.tag} is accessed through a DOM alias; review property assignments before inserting v7 compatibility defaults.`})}for(const token of openingTokens){const profile=profiles.get(token.tag);if(!profile)continue;const opening=text.slice(token.nameEnd,token.end),patterns=[/\{\s*\.\.\.[^}]+\}/g,/\bv-bind\s*=\s*(?:"[^"]*"|'[^']*'|\{[^}]*\})/g];for(const pattern of patterns)for(const match of opening.matchAll(pattern)){const offset=token.nameEnd+match.index,key=localMigrationKey(profile.origin,token.tag);blocked.add(key),uses.push({key,tag:token.tag,offset,warningCode:"DYNAMIC_VALUE_REVIEW",message:`${token.tag} has an opaque attribute spread that may already provide a migrated default; expand or review it before running this profile.`})}}return{uses,blocked}}function migrateLocalText(original,contract,options){const file=options.file??"<memory>",origin=options.origin,profiles=contract.localMigrations.get(origin);invariant(profiles,`unknown local migration origin ${String(origin)}`);const starts=lineStarts(original),ignoredRanges=commentRanges(original),openingTokens=scanAllOpeningTags(original,ignoredRanges).filter(token=>profiles.has(token.tag)),hazards=scanLocalMigrationHazards(original,profiles,ignoredRanges,openingTokens),blocked=new Set([...options.blockedLocalMigrations??[],...hazards.blocked]),edits=[],changes=[],warnings=[],entry=(offset,tag,action,target,message,warningCode2=null,upstreamMember=null)=>reportEntry({textStarts:starts,file,offset,origin,upstreamTag:tag,upstreamMember,action,target,warningCode:warningCode2,message});for(const use of hazards.uses)warnings.push(entry(use.offset,use.tag,"manual-review",use.tag,use.message,use.warningCode));for(const token of openingTokens){const profile=profiles.get(token.tag),key=localMigrationKey(origin,token.tag);if(blocked.has(key)){warnings.push(entry(token.nameStart,token.tag,"manual-review",token.tag,`${token.tag} remains unchanged because the scanned target set contains an aliased or dynamic value requiring review.`,"MAPPING_REVIEW_BLOCKED"));continue}const attributes=parseTagAttributes(original,token),present=new Set(attributes.map(attribute=>localAttributeName(attribute.rawName))),insertions=[];for(const rule of profile.defaults)present.has(rule.member.toLowerCase())||(insertions.push(serializeLocalDefault(rule)),changes.push(entry(token.end,token.tag,"insert-default",`${rule.member}=${String(rule.value)}`,`Insert ${rule.member} to preserve the Lyra v7 default.`,null,rule.member)));if(insertions.length>0){const insertionOffset=original[token.end-1]==="/"?token.end-1:token.end;edits.push({start:insertionOffset,end:insertionOffset,replacement:` ${insertions.join(" ")}`})}}return changes.sort((left,right)=>left.line-right.line||left.column-right.column||left.action.localeCompare(right.action)),warnings.sort((left,right)=>left.line-right.line||left.column-right.column||left.warningCode.localeCompare(right.warningCode)),{content:finalizeEdits(original,edits),changes,warnings,usage:{webawesome:{automatic:0,manual:0},shoelace:{automatic:0,manual:0}},blockedMappings:new Set,blockedEcosystems:new Set,bareImportEcosystems:new Set,blockedLocalMigrations:blocked}}function scanUnrewrittenUpstreamReferences(text){const found=[],seen=new Set,push=(offset,upstreamTag,member,code,target,message)=>{const key=`${offset}:${code}`;seen.has(key)||(seen.add(key),found.push({offset,upstreamTag,member,code,target,message}))},TAG=/(?<![\w-])(?<tag>(?:wa|sl)-[a-z][a-z0-9-]*)(?![\w-])/g;for(const block of text.matchAll(/\bcss`/g)){const bodyStart=block.index+block[0].length,bodyEnd=text.indexOf("`",bodyStart);if(bodyEnd===-1)continue;const body=text.slice(bodyStart,bodyEnd);for(const match of body.matchAll(TAG)){const tag=match.groups.tag;push(bodyStart+match.index,tag,"css-template","CSS_IN_JS_SELECTOR_REVIEW",tag,`\`${tag}\` appears in a css\`\` template and was NOT rewritten. A rule keyed on a tag that no longer exists matches nothing, silently. Rename the selector by hand.`)}}for(const match of text.matchAll(/::slotted\(\s*([^)]*)\)/g))for(const inner of match[1].matchAll(TAG)){const tag=inner.groups.tag;push(match.index+match[0].indexOf(inner.groups.tag),tag,"::slotted","SLOTTED_SELECTOR_REVIEW",tag,`\`::slotted(${tag})\` was NOT rewritten and will match nothing after the migration. Rename it by hand.`)}const CALL=/\bthis(?:\s*\??\.\s*(?:shadowRoot|renderRoot))?\s*\??\.\s*(?:querySelector|querySelectorAll|closest|matches)\s*(?:<[^>\n]+>)?\(\s*(['"`])(?<selector>[^'"`]*)\1/g;for(const match of text.matchAll(CALL)){const selectorOffset=match.index+match[0].lastIndexOf(match.groups.selector);for(const inner of match.groups.selector.matchAll(TAG)){const tag=inner.groups.tag;push(selectorOffset+inner.index,tag,"selector-string","SELECTOR_STRING_REVIEW",tag,`\`${tag}\` appears in a DOM selector string that was NOT rewritten. The query will return null after the migration rather than throwing. Rename it by hand.`)}}const declaredHere=new Set([...text.matchAll(/(--(?:wa|sl)-[a-z0-9-]+)\s*:/g)].map(match=>match[1]));for(const match of text.matchAll(/--(?<ecosystem>wa|sl)-[a-z0-9-]+/g)){const token=match[0];declaredHere.has(token)||push(match.index,`${match.groups.ecosystem}-tokens`,"custom-property","UPSTREAM_TOKEN_REVIEW",token,`\`${token}\` is an upstream design token with no automatic Lyra equivalent, and was left as-is. A var() naming a token that no longer exists falls back to its second argument, or to nothing -- silently. Map it by hand against llms/tokens.md; note the spacing scales are offset by one step, so renaming by name alone tightens every gap.`)}return found}function migrateText(original,contract,options={}){if(options.origin)return migrateLocalText(original,contract,options);const file=options.file??"<memory>",rewriteBarePackages=options.rewriteBarePackages??new Set,rootRegistrationMappings=options.rootRegistrationMappings??new Map,starts=lineStarts(original),ignoredRanges=commentRanges(original),htmlStyleRanges=htmlTemplateStyleRanges(original),markupTokens=scanMarkupTags(original,ignoredRanges),allOpeningTokens=scanAllOpeningTags(original,ignoredRanges),apiReferences=scanApiTagReferences(original,ignoredRanges,options.domFactoryBindings),aliasReviews=scanAliasedRewriteReviews(original,contract,ignoredRanges),dynamicDefaultReviews=scanDynamicDefaultReviews(original,markupTokens,contract),conditionalBehaviorReviews=scanConditionalBehaviorReviews(original,markupTokens,ignoredRanges,contract),imports=scanImports(original,ignoredRanges,contract),bareImportPackages=new Set(imports.filter(imported=>imported.sideEffect&&!imported.subpath).map(imported=>imported.packageName)),blockedMappings=new Set([...options.blockedMappings??[],...aliasReviews.keys(),...dynamicDefaultReviews.keys()]),imperativeDefaultReviews=apiReferences.flatMap(reference=>{if(reference.kind!=="construction")return[];const mapping=contract.mappings.get(reference.tag),defaults=mapping?.rewrites.defaults.filter(rule=>rule.action==="insert-if-absent")??[];if(!mapping||!AUTO_CLASSIFICATIONS.has(mapping.classification)||defaults.length===0)return[];const assessment=assessImperativeDefaults(original,reference,defaults);return assessment.safe?[]:(blockedMappings.add(mapping.upstreamTag),[{reference,mapping,defaults,missing:assessment.missing}])}),registrationBlockedMappings=new Set(options.registrationBlockedMappings??[]),blockedEcosystems=new Set(options.blockedEcosystems??[]);for(const imported of imports){if(imported.sideEffect)continue;const upstreamTag=imported.subpath?deepImportTag(imported.specifier,imported.ecosystem):null,mapping=upstreamTag?contract.mappings.get(upstreamTag):null;mapping&&AUTO_CLASSIFICATIONS.has(mapping.classification)?blockedMappings.add(mapping.upstreamTag):imported.subpath||blockedEcosystems.add(imported.ecosystem)}const edits=[],changes=[],warnings=[],usage={webawesome:{automatic:0,manual:0},shoelace:{automatic:0,manual:0}},automaticMappings=new Set,deepRegistrationMappings=new Set,reportedOptionalPeers=new Set,reportOrigin=(upstreamTag,origin)=>origin??(upstreamTag?.startsWith("wa-")?"webawesome":upstreamTag?.startsWith("sl-")?"shoelace":null),change=(offset,upstreamTag,upstreamMember,action,target,message,origin=null)=>{changes.push(reportEntry({textStarts:starts,file,offset,origin:reportOrigin(upstreamTag,origin),upstreamTag,upstreamMember,action,target,message}))},warn=(offset,upstreamTag,upstreamMember,code,target,message,origin=null,behaviorReviewFlags=null)=>{warnings.push(reportEntry({textStarts:starts,file,offset,origin:reportOrigin(upstreamTag,origin),upstreamTag,upstreamMember,action:"manual-review",target,warningCode:code,behaviorReviewFlags,message}))},noteRuntimeRequirements=(mapping,offset)=>{for(const peer of mapping.target?.optionalPeers??[]){const key=`${mapping.targetTag}:${peer}`;reportedOptionalPeers.has(key)||(reportedOptionalPeers.add(key),warn(offset,mapping.upstreamTag,"runtime","OPTIONAL_PEER_REQUIRED",peer,`${mapping.targetTag} requires the optional peer package ${peer}; install a compatible version before relying on the migrated component.`))}},noteAutomatic=(mapping,offset)=>{automaticMappings.add(mapping.upstreamTag),noteRuntimeRequirements(mapping,offset)},addEdit=(start,end,replacement,details)=>{edits.push({start,end,replacement}),change(start,details.upstreamTag,details.upstreamMember,details.action,details.target,details.message,details.origin)},isBlocked=mapping=>!!mapping&&(blockedMappings.has(mapping.upstreamTag)||registrationBlockedMappings.has(mapping.upstreamTag)||blockedEcosystems.has(mapping.upstream)),isBlockedAutomatic=mapping=>AUTO_CLASSIFICATIONS.has(mapping?.classification)&&isBlocked(mapping),isAutomatic=mapping=>AUTO_CLASSIFICATIONS.has(mapping?.classification)&&!isBlocked(mapping),blockedMessage=mapping=>registrationBlockedMappings.has(mapping.upstreamTag)?`${mapping.upstreamTag} remains unchanged because the scanned target set does not prove registration for ${mapping.targetTag}. Include ${targetImport(mapping.target)} in the scan or migrate the file that owns its supported root registration import.`:`${mapping.upstreamTag} remains unchanged because the scanned target set contains a use that requires manual member, default, or import review.`,blockedWarningCode=mapping=>registrationBlockedMappings.has(mapping.upstreamTag)?"REGISTRATION_CLOSURE_REQUIRED":"MAPPING_REVIEW_BLOCKED";for(const{mapping,uses}of aliasReviews.values())for(const use of uses)warn(use.offset,mapping.upstreamTag,use.upstreamMember,"ALIASED_MEMBER_REVIEW",use.target,use.message);for(const{mapping,uses}of dynamicDefaultReviews.values())for(const use of uses)warn(use.offset,mapping.upstreamTag,use.upstreamMember,"DYNAMIC_VALUE_REVIEW",use.target,use.message);for(const{reference,mapping,defaults,missing}of imperativeDefaultReviews)warn(reference.start,mapping.upstreamTag,null,"IMPERATIVE_DEFAULT_REVIEW",mapping.targetTag,`${mapping.upstreamTag} is constructed imperatively and requires explicit migrated defaults (${defaults.map(rule=>`${rule.member}=${String(rule.value)}`).join(", ")}); review the created element before renaming this mapping. No unconditional immediate assignment was proven for ${missing.map(rule=>rule.member).join(", ")}.`);const openingTokens=[],manualOpeningTags=new Set;for(const token of markupTokens){const mapping=contract.mappings.get(token.tag),ecosystem=ecosystemForTag(token.tag);if(!isAutomatic(mapping)){if(mapping&¬eRuntimeRequirements(mapping,token.nameStart),token.closing&&manualOpeningTags.has(token.tag))continue;token.closing||manualOpeningTags.add(token.tag),usage[ecosystem].manual+=1,warn(token.nameStart,token.tag,null,isBlockedAutomatic(mapping)?blockedWarningCode(mapping):warningCode(mapping),mapping?.targetTag??null,isBlockedAutomatic(mapping)?blockedMessage(mapping):mappingMessage(mapping),null,mapping?.parity?.behaviorReviewFlags);continue}usage[ecosystem].automatic+=1,noteAutomatic(mapping,token.nameStart),addEdit(token.nameStart,token.nameEnd,mapping.targetTag,{upstreamTag:token.tag,upstreamMember:null,action:"rewrite-tag",target:mapping.targetTag,message:`Rename ${token.tag} to ${mapping.targetTag}.`});const behaviorReview=conditionalBehaviorReviews.get(token.start);behaviorReview&&warn(token.nameStart,mapping.upstreamTag,null,"BEHAVIOR_REVIEW_REQUIRED",mapping.targetTag,behaviorReview.message,null,behaviorReview.flags),!token.closing&&REDUCED_MOTION_REVIEW_TAGS.has(token.tag)&&warn(token.nameStart,mapping.upstreamTag,null,"BEHAVIOR_REVIEW_REQUIRED",mapping.targetTag,reducedMotionReviewMessage(mapping.upstreamTag),null,["reduced-motion-default"]),!token.closing&&ICON_VOCABULARY_REVIEW_TAGS.has(token.tag)&&warn(token.nameStart,mapping.upstreamTag,null,"BEHAVIOR_REVIEW_REQUIRED",mapping.targetTag,iconVocabularyReviewMessage(mapping.upstreamTag),null,["icon-name-vocabulary"]),token.closing||openingTokens.push({token,mapping,attributes:parseTagAttributes(original,token)})}for(const reference of apiReferences){const mapping=contract.mappings.get(reference.tag),ecosystem=ecosystemForTag(reference.tag);if(!isAutomatic(mapping)){mapping&¬eRuntimeRequirements(mapping,reference.start),usage[ecosystem].manual+=1,warn(reference.start,reference.tag,null,isBlockedAutomatic(mapping)?blockedWarningCode(mapping):warningCode(mapping),mapping?.targetTag??null,isBlockedAutomatic(mapping)?blockedMessage(mapping):mappingMessage(mapping),null,mapping?.parity?.behaviorReviewFlags);continue}usage[ecosystem].automatic+=1,noteAutomatic(mapping,reference.start),addEdit(reference.start,reference.end,mapping.targetTag,{upstreamTag:reference.tag,upstreamMember:null,action:"rewrite-tag",target:mapping.targetTag,message:`Rename ${reference.tag} to ${mapping.targetTag}.`}),REDUCED_MOTION_REVIEW_TAGS.has(reference.tag)&&warn(reference.start,mapping.upstreamTag,null,"BEHAVIOR_REVIEW_REQUIRED",mapping.targetTag,reducedMotionReviewMessage(mapping.upstreamTag),null,["reduced-motion-default"]),ICON_VOCABULARY_REVIEW_TAGS.has(reference.tag)&&warn(reference.start,mapping.upstreamTag,null,"BEHAVIOR_REVIEW_REQUIRED",mapping.targetTag,iconVocabularyReviewMessage(mapping.upstreamTag),null,["icon-name-vocabulary"])}for(const{token,mapping,attributes}of openingTokens){for(const attribute of attributes){const rule=namedRule(attribute.rawName,mapping);rule&&addEdit(attribute.nameStart,attribute.nameEnd,rule.replacement,{upstreamTag:mapping.upstreamTag,upstreamMember:rule.from,action:memberAction(rule.section),target:rule.to,message:`Rewrite ${rule.section} member ${rule.from} to ${rule.to}.`})}const insertions=[];for(const rule of mapping.rewrites.defaults){const variants=new Set([rule.member,kebabCase(rule.member),camelCase(rule.member)]),attribute=attributes.find(entry=>variants.has(entry.rawName.replace(/^[.?:]/,"")));if(rule.action==="insert-if-absent"){if(attribute)continue;insertions.push(`${kebabCase(rule.member)}="${String(rule.value).replaceAll("&","&").replaceAll('"',""")}"`),change(token.end,mapping.upstreamTag,rule.member,"insert-default",`${rule.member}=${String(rule.value)}`,`Insert ${rule.member} to preserve the upstream default.`);continue}!attribute||attribute.valueKind==="boolean"||(isDynamicAttribute(attribute)?(usage[mapping.upstream].manual+=1,warn(attribute.valueStart,mapping.upstreamTag,rule.member,"DYNAMIC_VALUE_REVIEW",String(rule.to),`The ${rule.member} value is dynamic; replace ${String(rule.from)} with ${String(rule.to)} at its source.`)):attribute.value===String(rule.from)&&addEdit(attribute.valueStart,attribute.valueEnd,String(rule.to),{upstreamTag:mapping.upstreamTag,upstreamMember:rule.member,action:"replace-default",target:String(rule.to),message:`Replace ${rule.member} value ${String(rule.from)} with ${String(rule.to)}.`}))}if(insertions.length){const insertionOffset=original[token.end-1]==="/"?token.end-1:token.end;edits.push({start:insertionOffset,end:insertionOffset,replacement:` ${insertions.join(" ")}`})}}const stack=[],pairs=[];for(const token of markupTokens)if(!token.closing&&!token.selfClosing)stack.push(token);else if(token.closing)for(let index=stack.length-1;index>=0;index-=1){if(stack[index].tag!==token.tag)continue;const[opening]=stack.splice(index,1);pairs.push({opening,closing:token});break}for(const{opening,closing}of pairs){const mapping=contract.mappings.get(opening.tag);if(!(!isAutomatic(mapping)||!mapping.rewrites.slots.length)){for(const descendant of allOpeningTokens)if(!(descendant.start<=opening.end||descendant.end>=closing.start))for(const attribute of parseTagAttributes(original,descendant))if(attribute.rawName==="slot"&&attribute.valueKind==="literal"){const rule=mapping.rewrites.slots.find(entry=>entry.from===attribute.value);rule&&addEdit(attribute.valueStart,attribute.valueEnd,rule.to,{upstreamTag:mapping.upstreamTag,upstreamMember:rule.from,action:"rewrite-slot",target:rule.to,message:`Rewrite slot ${rule.from} to ${rule.to}.`})}else{const prefix=attribute.rawName.startsWith("v-slot:")?"v-slot:":attribute.rawName.startsWith("#")?"#":null;if(!prefix)continue;const name=attribute.rawName.slice(prefix.length),rule=mapping.rewrites.slots.find(entry=>entry.from===name);rule&&addEdit(attribute.nameStart,attribute.nameEnd,`${prefix}${rule.to}`,{upstreamTag:mapping.upstreamTag,upstreamMember:rule.from,action:"rewrite-slot",target:rule.to,message:`Rewrite slot ${rule.from} to ${rule.to}.`})}}}const cssRule=/(?<selector>[^{}]+)\{(?<body>[^{}]*)\}/g;for(const match of original.matchAll(cssRule)){if(insideRanges(match.index,ignoredRanges))continue;const selectorStart=match.index,bodyStart=match.index+match[0].indexOf(match.groups.body);for(const mapping of contract.mappings.values()){const tagPattern=new RegExp(`(?<![a-z0-9-])${mapping.upstreamTag}(?![a-z0-9-])`,"g"),selectorMatches=[...match.groups.selector.matchAll(tagPattern)].filter(tagMatch=>{const offset=selectorStart+tagMatch.index;if(insideRanges(offset,ignoredRanges))return!1;const quoted=quotedRangeContaining(original,offset);return quoted?insideRanges(offset,htmlStyleRanges)&&!insideTemplateExpression(original,quoted[0],offset):!0});if(selectorMatches.length){if(!isAutomatic(mapping)){usage[mapping.upstream].manual+=selectorMatches.length;for(const tagMatch of selectorMatches){const offset=selectorStart+tagMatch.index;noteRuntimeRequirements(mapping,offset),warn(offset,mapping.upstreamTag,null,isBlockedAutomatic(mapping)?blockedWarningCode(mapping):warningCode(mapping),mapping.targetTag,isBlockedAutomatic(mapping)?blockedMessage(mapping):mappingMessage(mapping),null,mapping?.parity?.behaviorReviewFlags)}continue}for(const tagMatch of selectorMatches){const offset=selectorStart+tagMatch.index;if(noteAutomatic(mapping,offset),mapping.upstreamTag==="wa-random-content"){const flags=["host-layout","multi-item-layout"];warn(offset,mapping.upstreamTag,"selector","BEHAVIOR_REVIEW_REQUIRED",mapping.targetTag,"Review this selector after migration: Web Awesome lays candidates out through the host, while Lyra uses a block host and owns simultaneous-item flex layout inside its shadow base.",null,flags)}addEdit(offset,offset+mapping.upstreamTag.length,mapping.targetTag,{upstreamTag:mapping.upstreamTag,upstreamMember:null,action:"rewrite-tag",target:mapping.targetTag,message:`Rename ${mapping.upstreamTag} selector to ${mapping.targetTag}.`})}for(const rule of mapping.rewrites.parts){const pattern=new RegExp(`(?<=::part\\()${rule.from.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}(?=\\))`,"g");for(const partMatch of match.groups.selector.matchAll(pattern)){const offset=selectorStart+partMatch.index;insideRanges(offset,ignoredRanges)||addEdit(offset,offset+rule.from.length,rule.to,{upstreamTag:mapping.upstreamTag,upstreamMember:rule.from,action:"rewrite-part",target:rule.to,message:`Rewrite CSS part ${rule.from} to ${rule.to}.`})}}for(const rule of mapping.rewrites.cssProperties){const pattern=new RegExp(`${rule.from.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}(?![a-z0-9-])`,"g");for(const propertyMatch of match.groups.body.matchAll(pattern)){const offset=bodyStart+propertyMatch.index;insideRanges(offset,ignoredRanges)||addEdit(offset,offset+rule.from.length,rule.to,{upstreamTag:mapping.upstreamTag,upstreamMember:rule.from,action:"rewrite-css-property",target:rule.to,message:`Rewrite CSS custom property ${rule.from} to ${rule.to}.`})}}}}}for(const mapping of contract.mappings.values()){if(!isAutomatic(mapping))continue;const owner=`(?:document\\.)?querySelector(?:<[^>\\n]+>)?\\(\\s*(['"])${mapping.upstreamTag.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\1\\s*\\)`;for(const[section,suffix]of[["methods","(?=\\s*\\()"],["properties","(?=\\s*(?:=|\\.|\\?|;|$))"]])for(const rule of mapping.rewrites[section]){const escaped=rule.from.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),regex=new RegExp(`${owner}\\s*(?:\\?\\.|!\\s*\\.|\\.)\\s*(?<member>${escaped})${suffix}`,"g");for(const memberMatch of original.matchAll(regex)){if(insideRanges(memberMatch.index,ignoredRanges))continue;const relative=memberMatch[0].lastIndexOf(memberMatch.groups.member),offset=memberMatch.index+relative;addEdit(offset,offset+rule.from.length,rule.to,{upstreamTag:mapping.upstreamTag,upstreamMember:rule.from,action:memberAction(section),target:rule.to,message:`Rewrite ${section} member ${rule.from} to ${rule.to}.`})}}for(const rule of mapping.rewrites.events){const escaped=rule.from.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),regex=new RegExp(`${owner}\\s*(?:\\?\\.|!\\s*\\.|\\.)\\s*(?:add|remove)EventListener\\(\\s*(['"])(?<member>${escaped})\\2`,"g");for(const eventMatch of original.matchAll(regex)){if(insideRanges(eventMatch.index,ignoredRanges))continue;const relative=eventMatch[0].lastIndexOf(eventMatch.groups.member),offset=eventMatch.index+relative;addEdit(offset,offset+rule.from.length,rule.to,{upstreamTag:mapping.upstreamTag,upstreamMember:rule.from,action:"rewrite-event",target:rule.to,message:`Rewrite event ${rule.from} to ${rule.to}.`})}}}for(const imported of imports){const{ecosystem}=imported;if(!imported.sideEffect){usage[ecosystem].manual+=1,warn(imported.start,imported.subpath?deepImportTag(imported.specifier,ecosystem):null,"module","IMPORT_BINDING_REVIEW_REQUIRED",null,`The ${imported.kind} import has runtime/type bindings whose exported names cannot be inferred safely.`,ecosystem);continue}if(!imported.subpath){const canRewrite=rewriteBarePackages.has(ecosystem)||rewriteBarePackages.has(imported.packageName),registeredMappings=rootRegistrationMappings.get(imported.packageName)??rootRegistrationMappings.get(ecosystem)??new Set,closure=registrationClosure(contract,registeredMappings);if(canRewrite&&closure.length>0){addEdit(imported.start,imported.end,closure[0],{upstreamTag:null,origin:ecosystem,upstreamMember:"module",action:"rewrite-import",target:closure[0],message:`Rewrite ${imported.packageName} root registration import to its proven Lyra registration closure.`});const lineEnding=original.includes(`\r
|
|
15
|
+
`,closingQuoteEnd),lineEnd=lineBreak===-1?text.length:lineBreak,semicolon=text.indexOf(";",closingQuoteEnd),statementEnd=semicolon!==-1&&semicolon<lineEnd?semicolon+1:lineEnd;imports.push({...context,ecosystem:identity.ecosystem,tiers:identity.tiers,packageName:match.groups.specifier,specifier,subpath:match.groups.subpath??"",quote:match[1],start:quoteStart+1,end:quoteStart+1+specifier.length,statementEnd})}return imports}function parseNamedModuleBindings(source){const bindings=[],withoutComments=source.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/[^\n\r]*/g,"");for(const rawEntry of withoutComments.split(",")){const entry=rawEntry.trim().replace(/^type\s+/,"");if(!entry)continue;const match=entry.match(new RegExp(`^(?<imported>${JS_IDENTIFIER})(?:\\s+as\\s+(?<local>${JS_IDENTIFIER}))?$`));match&&bindings.push({imported:match.groups.imported,local:match.groups.local??match.groups.imported})}return bindings}function scanModuleFactoryLinks(text,ignoredRanges){const imports=[],localExports=[],reexports=[],starExports=[],addMatches=(pattern,visit)=>{for(const match of text.matchAll(pattern))!insideRanges(match.index,ignoredRanges)&&!quotedRangeContaining(text,match.index)&&visit(match)};return addMatches(/\bimport\s+(?!type\b)\{(?<bindings>[\s\S]*?)\}\s*from\s*(?<quote>['"])(?<source>[^'"]+)\k<quote>/g,match=>{for(const binding of parseNamedModuleBindings(match.groups.bindings))imports.push({source:match.groups.source,imported:binding.imported,local:binding.local})}),addMatches(new RegExp(`\\bimport\\s+(?!type\\b)(?<local>${JS_IDENTIFIER})(?:\\s*,\\s*(?:\\{[\\s\\S]*?\\}|\\*\\s+as\\s+${JS_IDENTIFIER}))?\\s+from\\s*(?<quote>['"])(?<source>[^'"]+)\\k<quote>`,"g"),match=>imports.push({source:match.groups.source,imported:"default",local:match.groups.local})),addMatches(new RegExp(`\\bimport\\s+\\*\\s+as\\s+(?<local>${JS_IDENTIFIER})\\s+from\\s*(?<quote>['"])(?<source>[^'"]+)\\k<quote>`,"g"),match=>imports.push({source:match.groups.source,imported:"*",local:match.groups.local})),addMatches(/\bexport\s*\{(?<bindings>[\s\S]*?)\}\s*from\s*(?<quote>['"])(?<source>[^'"]+)\k<quote>/g,match=>{for(const binding of parseNamedModuleBindings(match.groups.bindings))reexports.push({source:match.groups.source,imported:binding.imported,exported:binding.local})}),addMatches(/\bexport\s*\*\s*from\s*(?<quote>['"])(?<source>[^'"]+)\k<quote>/g,match=>starExports.push(match.groups.source)),addMatches(/\bexport\s*\{(?<bindings>[\s\S]*?)\}(?!\s*from\b)/g,match=>{for(const binding of parseNamedModuleBindings(match.groups.bindings))localExports.push({local:binding.imported,exported:binding.local})}),addMatches(new RegExp(`\\bexport\\s+(?:const|let|var|function)\\s+(?<name>${JS_IDENTIFIER})`,"g"),match=>localExports.push({local:match.groups.name,exported:match.groups.name})),addMatches(new RegExp(`\\bexport\\s+default\\s+(?<name>${JS_IDENTIFIER})(?![$\\w])`,"g"),match=>localExports.push({local:match.groups.name,exported:"default"})),{imports,localExports,reexports,starExports}}const MODULE_SOURCE_EXTENSIONS=[".ts",".tsx",".mts",".cts",".js",".jsx",".mjs",".cjs"];function resolveScannedModule(fromFile,specifier,scannedFiles){if(!specifier.startsWith("."))return null;const base=path.resolve(path.dirname(fromFile),specifier),extension=path.extname(base),candidates=[base];if(extension){const stem=base.slice(0,-extension.length);for(const candidateExtension of MODULE_SOURCE_EXTENSIONS)candidates.push(`${stem}${candidateExtension}`)}else for(const candidateExtension of MODULE_SOURCE_EXTENSIONS)candidates.push(`${base}${candidateExtension}`);for(const candidateExtension of MODULE_SOURCE_EXTENSIONS)candidates.push(path.join(base,`index${candidateExtension}`));return candidates.find(candidate=>scannedFiles.has(candidate))??null}function factoryMapEqual(left,right){return left.size===right.size&&[...left].every(([name,kind])=>right.get(name)===kind)}function buildProjectDomFactoryBindings(originals){const modules=new Map;for(const[file,text]of originals){const resolvedFile=path.resolve(file),ignoredRanges=commentRanges(text);modules.set(resolvedFile,{text,ignoredRanges,links:scanModuleFactoryLinks(text,ignoredRanges)})}const scannedFiles=new Set(modules.keys()),exportedBindings=new Map([...scannedFiles].map(file=>[file,new Map])),importedBindings=new Map([...scannedFiles].map(file=>[file,new Map]));for(let pass=0;pass<Math.max(4,modules.size*4);pass+=1){let changed=!1;for(const[file,module]of modules){const seeds=new Map;for(const imported of module.links.imports){const target=resolveScannedModule(file,imported.source,scannedFiles);if(!target)continue;const targetExports=exportedBindings.get(target);if(imported.imported==="*")for(const[name,kind]of targetExports)seeds.set(`${imported.local}.${name}`,kind);else{const kind=targetExports.get(imported.imported);kind&&seeds.set(imported.local,kind)}}const analysis=scanLocalDomFactoryBindings(module.text,module.ignoredRanges,seeds);factoryMapEqual(importedBindings.get(file),seeds)||(importedBindings.set(file,seeds),changed=!0);const nextExports=new Map;for(const exported of module.links.localExports){const kind=analysis.moduleBindings.get(exported.local);kind&&nextExports.set(exported.exported,kind)}for(const reexported of module.links.reexports){const target=resolveScannedModule(file,reexported.source,scannedFiles),kind=target?exportedBindings.get(target).get(reexported.imported):null;kind&&nextExports.set(reexported.exported,kind)}for(const source of module.links.starExports){const target=resolveScannedModule(file,source,scannedFiles);if(target)for(const[name,kind]of exportedBindings.get(target))name!=="default"&&nextExports.set(name,kind)}factoryMapEqual(exportedBindings.get(file),nextExports)||(exportedBindings.set(file,nextExports),changed=!0)}if(!changed)break}return importedBindings}function finalizeEdits(original,edits){const unique=[],seen=new Set;for(const edit of edits){const key=`${edit.start}:${edit.end}:${edit.replacement}`;seen.has(key)||(seen.add(key),unique.push(edit))}unique.sort((left,right)=>left.start-right.start||left.end-right.end);for(let index=1;index<unique.length;index+=1)if(unique[index].start<unique[index-1].end)throw new Error(`Migration rewrite conflict at offsets ${unique[index-1].start} and ${unique[index].start}`);const parts=[];let cursor=original.length;for(const edit of[...unique].sort((left,right)=>right.start-left.start||right.end-left.end))parts.push(original.slice(edit.end,cursor),edit.replacement),cursor=edit.start;return parts.push(original.slice(0,cursor)),parts.reverse().join("")}function mappingMessage(mapping){return mapping?mapping.rationale||`The ${mapping.classification} mapping requires manual review.`:"No pinned inventory mapping exists for this upstream tag."}function localMigrationKey(origin,tag){return`${origin}:${tag}`}function originLabel(origin){return String(origin).replace(/^lyra-/,"")}function localAttributeName(rawName){return rawName.replace(/^(?:v-bind:|bind:|[.?:])/,"").toLowerCase()}function serializeLocalDefault(rule){if(rule.value===!0)return rule.member;const value=String(rule.value).replaceAll("&","&").replaceAll('"',""");return`${rule.member}="${value}"`}function scanLocalMigrationHazards(text,profiles,ignoredRanges,openingTokens){const uses=[],blocked=new Set,tags=new Set(profiles.keys()),apiPattern=/\b(?:(?:[$A-Z_a-z][$\w]*)\s*(?:\?\.|\.)\s*(?:createElement|querySelector(?:All)?)|(?:createElement|querySelector(?:All)?)|customElements\.(?:get|whenDefined))\s*(?:<[^>\n]+>)?\(\s*(['"])(?<tag>lr-[a-z][a-z0-9-]*)\1/g;for(const match of text.matchAll(apiPattern)){if(insideRanges(match.index,ignoredRanges)||!tags.has(match.groups.tag))continue;const offset=match.index+match[0].lastIndexOf(match.groups.tag),key=localMigrationKey(profiles.get(match.groups.tag).origin,match.groups.tag);blocked.add(key),uses.push({key,tag:match.groups.tag,offset,warningCode:"ALIASED_MEMBER_REVIEW",message:`${match.groups.tag} is accessed through a DOM alias; review property assignments before inserting ${originLabel(profiles.get(match.groups.tag).origin)} compatibility defaults.`})}for(const token of openingTokens){const profile=profiles.get(token.tag);if(!profile)continue;const opening=text.slice(token.nameEnd,token.end),patterns=[/\{\s*\.\.\.[^}]+\}/g,/\bv-bind\s*=\s*(?:"[^"]*"|'[^']*'|\{[^}]*\})/g];for(const pattern of patterns)for(const match of opening.matchAll(pattern)){const offset=token.nameEnd+match.index,key=localMigrationKey(profile.origin,token.tag);blocked.add(key),uses.push({key,tag:token.tag,offset,warningCode:"DYNAMIC_VALUE_REVIEW",message:`${token.tag} has an opaque attribute spread that may already provide a migrated default; expand or review it before running this profile.`})}}return{uses,blocked}}function migrateLocalText(original,contract,options){const file=options.file??"<memory>",origin=options.origin,profiles=contract.localMigrations.get(origin);invariant(profiles,`unknown local migration origin ${String(origin)}`);const starts=lineStarts(original),ignoredRanges=commentRanges(original),openingTokens=scanAllOpeningTags(original,ignoredRanges).filter(token=>profiles.has(token.tag)),hazards=scanLocalMigrationHazards(original,profiles,ignoredRanges,openingTokens),blocked=new Set([...options.blockedLocalMigrations??[],...hazards.blocked]),edits=[],changes=[],warnings=[],entry=(offset,tag,action,target,message,warningCode2=null,upstreamMember=null)=>reportEntry({textStarts:starts,file,offset,origin,upstreamTag:tag,upstreamMember,action,target,warningCode:warningCode2,message});for(const use of hazards.uses)warnings.push(entry(use.offset,use.tag,"manual-review",use.tag,use.message,use.warningCode));for(const token of openingTokens){const profile=profiles.get(token.tag),key=localMigrationKey(origin,token.tag);if(blocked.has(key)){warnings.push(entry(token.nameStart,token.tag,"manual-review",token.tag,`${token.tag} remains unchanged because the scanned target set contains an aliased or dynamic value requiring review.`,"MAPPING_REVIEW_BLOCKED"));continue}const attributes=parseTagAttributes(original,token),present=new Set(attributes.map(attribute=>localAttributeName(attribute.rawName))),insertions=[];for(const rule of profile.defaults)present.has(rule.member.toLowerCase())||(insertions.push(serializeLocalDefault(rule)),changes.push(entry(token.end,token.tag,"insert-default",`${rule.member}=${String(rule.value)}`,`Insert ${rule.member} to preserve the Lyra ${originLabel(origin)} default.`,null,rule.member)));if(insertions.length>0){const insertionOffset=original[token.end-1]==="/"?token.end-1:token.end;edits.push({start:insertionOffset,end:insertionOffset,replacement:` ${insertions.join(" ")}`})}}return changes.sort((left,right)=>left.line-right.line||left.column-right.column||left.action.localeCompare(right.action)),warnings.sort((left,right)=>left.line-right.line||left.column-right.column||left.warningCode.localeCompare(right.warningCode)),{content:finalizeEdits(original,edits),changes,warnings,usage:{webawesome:{automatic:0,manual:0},shoelace:{automatic:0,manual:0}},blockedMappings:new Set,blockedEcosystems:new Set,bareImportEcosystems:new Set,blockedLocalMigrations:blocked}}const RENAME_REVIEW="RENAME_REVIEW",RENAME_TARGET_SHARED_REVIEW="RENAME_TARGET_SHARED_REVIEW",NAME_GAINED_OWNER_REVIEW="NAME_GAINED_OWNER_REVIEW",POLARITY_REVIEW="POLARITY_REVIEW",DETAIL_SHAPE_REVIEW="DETAIL_SHAPE_REVIEW",DEPRECATED_MEMBER_REVIEW="DEPRECATED_MEMBER_REVIEW",DEPRECATED_CONTENT_REVIEW="DEPRECATED_CONTENT_REVIEW",RENAME_CONFLICT_REVIEW="RENAME_CONFLICT_REVIEW",UNUSED_ACKNOWLEDGEMENT="UNUSED_ACKNOWLEDGEMENT",REVIEW_ACKNOWLEDGEMENT=/lyra-migrate-reviewed:\s*([^\n]*)/g,ACKNOWLEDGEMENT_TOKEN=/^([A-Z][A-Z0-9_]*):(\S+)$/,DEFAULT_SLOT_LABEL="#default",LISTENER_CALL_BEFORE=/(?:\b(?:add|remove)EventListener|\bHostListener)\s*\(\s*$/,LISTENER_RECEIVER_BEFORE=/([A-Za-z_$][\w$]*(?:\s*(?:\?\.|\.)\s*#?[A-Za-z_$][\w$]*|\s*\([^()\n]*\)|\s*!(?!=))*)\s*(?:\?\.|\.)\s*(?:add|remove)EventListener\s*\(\s*$/,LISTENER_HANDLER_AFTER=/^\s*,\s*([A-Za-z_$][\w$]*(?:\s*(?:\?\.|\.)\s*#?[A-Za-z_$][\w$]*)*)\s*[,)]/,EVENT_CONSTRUCTION=/\bnew\s+(?:CustomEvent|Event)\s*(?:<[^>\n]+>)?\(\s*(['"`])(lr-[a-z0-9]+(?:-[a-z0-9]+)*)\1/g,TAG_API_CALL_BEFORE=/(?:\b(?:createElement|querySelector(?:All)?|closest|matches|unsafeStatic|literal)|\bcustomElements\s*\.\s*(?:get|whenDefined|define))\s*(?:<[^>\n]+>)?\(\s*$/,ANCHORED_MEMBER=/\b(?:querySelector|closest|createElement)\s*(?:<[^>\n]+>)?\(\s*(['"`])(lr-[a-z0-9]+(?:-[a-z0-9]+)*)\1\s*\)\s*!?\s*(?:\?\.|\.)\s*([A-Za-z_$][\w$]*)/g,ATTRIBUTE_METHODS=new Set(["setAttribute","getAttribute","hasAttribute","removeAttribute","toggleAttribute"]),LISTENER_METHODS=new Set(["addEventListener","removeEventListener"]),STYLESHEET_FILE=/\.(?:css|scss|sass|less|pcss|styl)$/i,MARKUP_FILE=/\.(?:html?|xhtml|md|markdown|vue|svelte)$/i,ATTRIBUTE_SEMANTICS_FILE=/\.(?:html?|xhtml|md|markdown)$/i,LIT_TEMPLATE_FILE=/\.(?:[cm]?[jt]s)$/i,SELECTOR_LOOKBEHIND=256,ackName=member=>member===""?DEFAULT_SLOT_LABEL:String(member);function mergeRanges(ranges){const merged=[];for(const[start,end]of[...ranges].sort((left,right)=>left[0]-right[0])){const last=merged.at(-1);last&&start<=last[1]?last[1]=Math.max(last[1],end):merged.push([start,end])}return merged}function rangeTester(merged){return offset=>{let low=0,high=merged.length-1;for(;low<=high;){const middle=low+high>>1;if(merged[middle][1]<=offset)low=middle+1;else if(merged[middle][0]>offset)high=middle-1;else return!0}return!1}}function templateLiteralEnd(text,open){let index=open+1;for(;index<text.length;){const character=text[index];if(character==="\\")index+=2;else{if(character==="`")return index+1;character==="$"&&text[index+1]==="{"?index=templateExpressionEnd(text,index+2):index+=1}}return text.length}function templateExpressionEnd(text,index){let depth=1;for(;index<text.length;){const character=text[index];if(character==="`"){index=templateLiteralEnd(text,index);continue}if(character==='"'||character==="'"){for(index+=1;index<text.length&&text[index]!==character&&text[index]!==`
|
|
16
|
+
`;)index+=text[index]==="\\"?2:1;index+=1;continue}if(character==="{")depth+=1;else if(character==="}"&&--depth===0)return index+1;index+=1}return text.length}function templateLiteralRanges(text,inComment){const ranges=[];let index=0;for(;index<text.length;){const character=text[index];if(inComment(index))index+=1;else if(character==="`"){const end=templateLiteralEnd(text,index);ranges.push([index,end]),index=end}else if(character==='"'||character==="'"){for(index+=1;index<text.length&&text[index]!==character&&text[index]!==`
|
|
17
|
+
`;)index+=text[index]==="\\"?2:1;index+=1}else index+=1}return ranges}function taggedTemplateRanges(text,tags,inComment){const ranges=[];for(const match of text.matchAll(new RegExp(`\\b(?:${tags.join("|")})\\s*\``,"g"))){if(inComment(match.index))continue;const open=match.index+match[0].length-1;ranges.push([open,templateLiteralEnd(text,open)])}return mergeRanges(ranges)}function renameIgnoredRanges(text,file){const base=mergeRanges(commentRanges(text)),inBase=rangeTester(base),extra=[],addHtmlComments=(start,end)=>{let index=text.indexOf("<!--",start);for(;index>=0&&index<end;){const close=text.indexOf("-->",index+4),finish=close<0||close+3>end?end:close+3;inBase(index)||extra.push([index,finish]),index=text.indexOf("<!--",finish)}};if(MARKUP_FILE.test(file))addHtmlComments(0,text.length);else if(!STYLESHEET_FILE.test(file))for(const[start,end]of templateLiteralRanges(text,inBase))addHtmlComments(start,end);return mergeRanges([...base,...extra])}function scanReviewAcknowledgements(text,ignoredRanges,starts,openingTokens){const tokenAt=new Map(openingTokens.map(token=>[token.start,token])),records=[],byLine=new Map,cover=(line,record)=>{const list=byLine.get(line)??[];list.includes(record)||list.push(record),byLine.set(line,list)};for(const[start,end]of ignoredRanges){const body=text.slice(start,end);if(!body.includes("lyra-migrate-reviewed:"))continue;const firstLine=locationAt(starts,start).line,lastLine=locationAt(starts,Math.max(start,end-1)).line;let next=end;for(;next<text.length&&/[\s}]/.test(text[next]);)next+=1;const followingTag=tokenAt.get(next),standalone=/^\s*\{?\s*$/.test(text.slice(starts[firstLine-1],start));for(const match of body.matchAll(REVIEW_ACKNOWLEDGEMENT)){const tokens=match[1].replace(/(?:\*\/|-->|\*\/\s*\})[\s\S]*$/,"").split(/[\s,]+/).filter(Boolean),record={offset:start+match.index,entries:tokens.map(token=>{const parsed=ACKNOWLEDGEMENT_TOKEN.exec(token);return{token,code:parsed?.[1]??null,name:parsed?.[2]??null,used:!1}})};records.push(record);for(let line=firstLine;line<=lastLine;line+=1)cover(line,record);if(standalone&&cover(lastLine+1,record),followingTag){const tagLast=locationAt(starts,followingTag.end).line;for(let line=locationAt(starts,followingTag.start).line;line<=tagLast;line+=1)cover(line,record)}}}return{consume(line,code,member){const name=ackName(member);for(const record of byLine.get(line)??[]){const entry=record.entries.find(candidate=>candidate.code===code&&candidate.name===name);if(entry)return entry.used=!0,!0}return!1},unused:()=>records.flatMap(record=>record.entries.filter(entry=>!entry.used).map(entry=>({offset:record.offset,entry})))}}function quotedStringRanges(text,inComment){const ranges=[];let index=0;for(;index<text.length;){const quote=text[index];if(inComment(index)||quote!=='"'&"e!=="'"&"e!=="`"){index+=1;continue}const start=index;for(index+=1;index<text.length&&text[index]!==quote&&(quote==="`"||text[index]!==`
|
|
18
|
+
`);)text[index]==="\\"&&(index+=1),index+=1;index+=1,quote!=="`"&&ranges.push([start,index])}return ranges}function stylesheetRanges(text,file,inComment){if(STYLESHEET_FILE.test(file))return[[0,text.length]];const ranges=[];for(const match of text.matchAll(/<style(?:\s[^>]*)?>[\s\S]*?<\/style\s*>/gi))ranges.push([match.index,match.index+match[0].length]);return mergeRanges([...ranges,...taggedTemplateRanges(text,["css"],inComment)])}function compoundTypeBefore(text,index){let start=index,brackets=0,parens=0;const limit=Math.max(0,index-SELECTOR_LOOKBEHIND);for(;start>limit;){const character=text[start-1];if(character==="]")brackets+=1;else if(character==="["){if(brackets===0)break;brackets-=1}else if(character===")")parens+=1;else if(character==="("){if(parens===0)break;parens-=1}else if(brackets===0&&parens===0&&/[\s,>+~{};'"`]/.test(character))break;start-=1}if(start===limit&&limit>0)return null;const compound=text.slice(start,index);return(/^:global\(\s*([A-Za-z][\w-]*)\s*\)$/.exec(compound)??/^([A-Za-z][\w-]*)/.exec(compound))?.[1].toLowerCase()??null}function mentionedTags(text,tags,inComment){const found=new Set;for(const tag of tags)for(const match of text.matchAll(new RegExp(`(?<![\\w-])${tag}(?![\\w-])`,"g")))if(!inComment(match.index)){found.add(tag);break}return found}function elementPairs(text,tag,inComment){const pairs=[],stack=[];for(const match of text.matchAll(new RegExp(`<(/)?${tag}(?=[\\s/>])`,"g"))){if(inComment(match.index))continue;const end=findTagEnd(text,match.index+match[0].length);if(!(end<0))if(match[1]){const opening=stack.pop();opening&&pairs.push({opening,closingStart:match.index})}else/\/\s*>$/.test(text.slice(match.index,end+1))||stack.push({start:match.index,end})}return pairs}function directChildTokens(text,start,end,inComment){const children=[],stack=[],pattern=/<(\/)?([A-Za-z][\w.:-]*)(?=[\s/>])/g;pattern.lastIndex=start;for(let match=pattern.exec(text);match&&match.index<end;match=pattern.exec(text)){if(inComment(match.index))continue;const tokenEnd=findTagEnd(text,match.index+match[0].length);if(tokenEnd<0||tokenEnd>=end)break;const name=match[2].toLowerCase();if(match[1]){const index=stack.lastIndexOf(name);index>=0&&(stack.length=index)}else{const nameStart=match.index+1;stack.length===0&&children.push({tag:name,start:match.index,nameStart,nameEnd:nameStart+match[2].length,end:tokenEnd}),!/\/\s*>$/.test(text.slice(match.index,tokenEnd+1))&&!VOID_HTML_TAGS.has(name)&&stack.push(name)}pattern.lastIndex=tokenEnd+1}return children}function openingTagBraceGroups(text,token){const groups=[];let index=token.nameEnd,quote=null;for(;index<token.end;){const character=text[index];if(quote){character==="\\"?index+=1:character===quote&&(quote=null),index+=1;continue}if(character==='"'||character==="'")quote=character;else if(character==="{"){const end=skipBalanced(text,index,"{","}");!/=\s*$/.test(text.slice(token.nameEnd,index))&&text[index-1]!=="$"&&groups.push({start:index,end,body:text.slice(index+1,end-1).trim()}),index=end;continue}index+=1}return groups}function markupEventName(rawName){if(/^\(lr-[a-z0-9-]+\)$/.test(rawName))return{name:rawName.slice(1,-1),offset:1};if(/^onlr-/.test(rawName))return{name:rawName.slice(2).replace(/Capture$/,""),offset:2};for(const[prefix,separator]of[["@","."],["v-on:","."],["on:","|"]])if(rawName.startsWith(prefix))return{name:rawName.slice(prefix.length).split(separator)[0],offset:prefix.length};return null}function markupMemberBinding(attribute){const raw=attribute.rawName;let match;if(raw.startsWith("?"))return{form:"attribute",offset:1,name:raw.slice(1),dynamic:!0};if(match=/^\[attr\.([^\]]+)\]$/.exec(raw))return{form:"attribute",offset:6,name:match[1],dynamic:!0};if(match=/^\[([A-Za-z_$][\w$]*)\]$/.exec(raw))return{form:"property",offset:1,name:match[1],dynamic:!0};if(raw.startsWith("."))return{form:"property",offset:1,name:raw.slice(1),dynamic:!0};if(raw.startsWith("bind:"))return{form:"property",offset:5,name:raw.slice(5).split("|")[0],dynamic:!0};for(const prefix of["v-bind:",":"])if(raw.startsWith(prefix))return{form:"member",offset:prefix.length,name:raw.slice(prefix.length).split(".")[0],dynamic:!0};if(!/^[A-Za-z][\w-]*$/.test(raw)||/^onlr-/.test(raw))return null;const dynamic=attribute.valueKind==="expression"||/\$\{|\{\{/.test(attribute.value??"");return{form:/[A-Z]/.test(raw)?"property":"attribute",offset:0,name:raw,dynamic}}function attributeEnd(text,attribute){if(attribute.valueEnd===null)return attribute.nameEnd;const quote=text[attribute.valueStart-1];return(quote==='"'||quote==="'")&&text[attribute.valueEnd]===quote?attribute.valueEnd+1:attribute.valueEnd}function boundMemberNames(attributes){const names=new Set;for(const attribute of attributes){const binding=markupMemberBinding(attribute);binding&&names.add(kebabCase(binding.name))}return names}function sortReportEntries(entries,codeKey){return entries.sort((left,right)=>left.line-right.line||left.column-right.column||String(left[codeKey]).localeCompare(String(right[codeKey])))}function migrateRenameText(original,contract,options){const file=options.file??"<memory>",origin=options.origin,profile=contract.renameProfiles.get(origin);invariant(profile,`unknown migration origin ${String(origin)}`);const result={content:original,changes:[],warnings:[],acknowledged:0,usage:{webawesome:{automatic:0,manual:0},shoelace:{automatic:0,manual:0}},blockedMappings:new Set,blockedEcosystems:new Set,bareImportEcosystems:new Set,blockedLocalMigrations:new Set,constructedEvents:new Set};if(profile.isEmpty)return result;const starts=lineStarts(original),ignoredRanges=renameIgnoredRanges(original,file),inComment=rangeTester(ignoredRanges),openingTokens=scanAllOpeningTags(original,ignoredRanges,inComment),acknowledgements=scanReviewAcknowledgements(original,ignoredRanges,starts,openingTokens),litTemplates=LIT_TEMPLATE_FILE.test(file)?rangeTester(taggedTemplateRanges(original,["html","svg"],inComment)):()=>!1,attributeSemantics=offset=>ATTRIBUTE_SEMANTICS_FILE.test(file)||litTemplates(offset),release=`Lyra ${profile.toMajor}`,removal=`${profile.aliasRemovalMajor}.0.0`;for(const match of original.matchAll(EVENT_CONSTRUCTION))!inComment(match.index)&&profile.renamesNamed("event",match[2]).length&&result.constructedEvents.add(match[2]);const constructedEvents=new Set([...options.constructedEvents??[],...result.constructedEvents]),listenerIdentity=(quote,name)=>{const receiver=LISTENER_RECEIVER_BEFORE.exec(original.slice(Math.max(0,quote-200),quote))?.[1],handler=LISTENER_HANDLER_AFTER.exec(original.slice(quote+name.length+2,quote+name.length+202))?.[1];return receiver&&handler?`${receiver.replace(/\s+/g,"")}\0${handler.replace(/\s+/g,"")}`:null},listenedNames=new Map;if(profile.data.renames.some(entry=>entry.kind==="event"))for(const match of original.matchAll(/(['"`])(lr-[a-z0-9]+(?:-[a-z0-9]+)*)\1/g)){if(inComment(match.index)||!LISTENER_CALL_BEFORE.test(original.slice(Math.max(0,match.index-120),match.index)))continue;const identity=listenerIdentity(match.index,match[2]);identity&&listenedNames.set(identity,new Set([...listenedNames.get(identity)??[],match[2]]))}const sameHandlerListens=(quote,name)=>{if(!listenedNames.size)return!1;const names=listenedNames.get(listenerIdentity(quote,name));return!!names&&profile.renamesNamed("event",name).some(entry=>names.has(entry.to))},edits=[],seenWarnings=new Set,handledStrings=new Set,handledMembers=new Set,unique=values=>[...new Set(values)],list=values=>unique(values).join(", "),soleTag=entries=>unique(entries.map(entry=>entry.tag)).length===1?entries[0].tag:null,hint=(code,member)=>`After review, mark the site with a "lyra-migrate-reviewed: ${code}:${ackName(member)}" comment.`,rewrite=(start,end,replacement,{tag,member,action,target,message})=>{edits.push({start,end,replacement}),result.changes.push(reportEntry({textStarts:starts,file,offset:start,origin,upstreamTag:tag,upstreamMember:member,action,target,message}))},warn=(offset,{tag=null,member,code,target=null,message})=>{const key=`${offset}:${code}:${member}`;if(!seenWarnings.has(key)){if(seenWarnings.add(key),acknowledgements.consume(locationAt(starts,offset).line,code,member)){result.acknowledged+=1;return}result.warnings.push(reportEntry({textStarts:starts,file,offset,origin,upstreamTag:tag,upstreamMember:member,action:"manual-review",target,warningCode:code,message:`${message} ${hint(code,member)}`}))}},reviewMessage=review=>`${review.kind==="component"?`<${review.tag}>`:review.kind==="slot"&&review.name===""?`Content in the default slot of ${review.tag}`:`The ${review.tag} ${review.kind} ${review.name}`} is deprecated and scheduled for removal in ${review.removalNotBefore}; migrate to ${review.replacement} by hand.`,reportReview=(offset,review)=>warn(offset,{tag:review.tag,member:review.name,code:DEPRECATED_MEMBER_REVIEW,target:review.replacement,message:reviewMessage(review)}),unownedReviews=(kind,name,offset,owner)=>{const own=owner?profile.reviewFor(owner,kind,name):null;if(own)reportReview(offset,own);else if(!profile.exposes(kind,name,owner))for(const review of profile.reviewsNamed(kind,name))reportReview(offset,review)},label=kind=>kind==="css-property"?"custom property":kind,detailNote=name=>{const details=profile.detailsNamed(name);return details.length?` Its ${release} detail also changed: ${details.map(entry=>`${entry.tag}: ${entry.summary}`).join(" ")}`:""},unownedMove=({kind,name,start,context,rewritable,action})=>{const renames=profile.renamesNamed(kind,name),targets=unique(renames.map(entry=>entry.to)),constructed=kind==="event"&&constructedEvents.has(name);if(rewritable&&!constructed&&profile.isGlobal(kind,name))return rewrite(start,start+name.length,targets[0],{tag:soleTag(renames),member:name,action,target:targets[0],message:`Rename ${name} to ${targets[0]}; every component exposing it renamed it, and no other component uses ${targets[0]}.`}),targets[0];const sourceKeepers=unique(targets.flatMap(to=>profile.sourceKeepers(kind,name,to))).filter(tag=>!renames.some(entry=>entry.tag===tag)),targetKeepers=unique(targets.flatMap(to=>profile.targetKeepers(kind,name,to))),shared=targets.length===1&&!sourceKeepers.length&&targetKeepers.length>0&&!renames.some(entry=>entry.polarity);return warn(start,{tag:soleTag(renames),member:name,code:shared?RENAME_TARGET_SHARED_REVIEW:RENAME_REVIEW,target:targets.join(" | "),message:(kind==="css-property"?"Custom properties inherit into nested components. ":context==="listener"?"This listener does not prove which element it listens to. ":"This string may name an event of any component. ")+`${list(renames.map(entry=>entry.tag))} renamed the ${label(kind)} ${name} to ${targets.join(" or ")}`+(sourceKeepers.length?`, while ${list(sourceKeepers)} still ${sourceKeepers.length===1?"uses":"use"} ${name}`:"")+(targetKeepers.length?`; ${list(targetKeepers)} already ${targetKeepers.length===1?"uses":"use"} ${targets.join(" and ")}, which this site would start reaching`:"")+(constructed?`; the scanned code also dispatches ${name} itself, so its listeners and dispatches must move together`:"")+`. Update it by hand for the components it is meant for; ${name} keeps working until ${removal}.`+(kind==="event"?detailNote(targets[0]):"")}),null},eventMove=(name,owner,rewritable=!0)=>{if(constructedEvents.has(name))return null;const own=owner?profile.renameFor(owner,"event",name):null;return own?profile.targetKeepers("event",name,own.to).length?null:own.to:!rewritable||!profile.renamesNamed("event",name).length||profile.exposes("event",name,owner)?null:profile.isGlobal("event",name)?profile.renamesNamed("event",name)[0].to:null},conflictMessage=(conflict,subject,to)=>conflict==="same-handler"?`${subject} already listens to ${to} with this same handler; renamed, the two identical registrations would collapse into one, so the handler would run once per activation instead of twice. Rename or remove this call, and its add or remove counterpart, together by hand.`:`${subject} already listens to ${to}; merge the two handlers by hand.`,eventSite=({name,start,owner,context,rewritable=!0,conflict=!1})=>{const own=owner?profile.renameFor(owner,"event",name):null;let current=name;if(own){const keepers=profile.targetKeepers("event",name,own.to);if(constructedEvents.has(name))warn(start,{tag:owner,member:name,code:RENAME_REVIEW,target:own.to,message:`Not renamed: the scanned code dispatches ${name} itself, so this listener would stop hearing it. Rename the dispatch and its listeners to ${own.to} together; ${name} keeps working until ${removal}.${detailNote(own.to)}`});else if(keepers.length)warn(start,{tag:owner,member:name,code:RENAME_TARGET_SHARED_REVIEW,target:own.to,message:`Not renamed: ${list(keepers)} also ${keepers.length===1?"dispatches":"dispatch"} ${own.to}, so this listener would start receiving their events when they are nested inside this ${owner}. Rename it by hand and ignore events whose target is not this ${owner}; ${name} keeps working until ${removal}.${detailNote(own.to)}`});else if(conflict)warn(start,{tag:owner,member:name,code:RENAME_CONFLICT_REVIEW,target:own.to,message:`${conflictMessage(conflict,`This ${owner}`,own.to)} ${name} keeps working until ${removal}.`});else{const narrowed=profile.sourceKeepers("event",name,own.to).filter(tag=>tag!==owner);rewrite(start,start+name.length,own.to,{tag:owner,member:name,action:"rewrite-event",target:own.to,message:`Rename the ${owner} event ${name} to ${own.to}.`+(narrowed.length?` ${list(narrowed)} still ${narrowed.length===1?"dispatches":"dispatch"} ${name}; this listener stops hearing theirs from nested elements.`:"")}),current=own.to}}else profile.renamesNamed("event",name).length&&!profile.exposes("event",name,owner)&&(conflict?warn(start,{tag:soleTag(profile.renamesNamed("event",name)),member:name,code:RENAME_CONFLICT_REVIEW,target:profile.renamesNamed("event",name)[0].to,message:conflictMessage(conflict,conflict==="same-handler"?"This receiver":"This element",profile.renamesNamed("event",name)[0].to)}):current=unownedMove({kind:"event",name,start,context,rewritable,action:"rewrite-event"})??name);const details=profile.detailsNamed(current),ownDetail=owner?profile.detailFor(owner,current):null;if(ownDetail||details.length&&!profile.exposes("event",current,owner)){const affected=ownDetail?[ownDetail]:details;warn(start,{tag:ownDetail?owner:soleTag(affected),member:current,code:DETAIL_SHAPE_REVIEW,target:current,message:`The ${current} event detail changed in ${release} and cannot be aliased: `+affected.map(entry=>`${entry.tag}: ${entry.summary}`).join(" ")+` Update this handler if it reads the detail of ${ownDetail?`the ${owner} event`:"one of these components"}.`})}const gained=current===name?profile.gainedOwners("event",name):[];gained.length&&!profile.exposes("event",name,owner)&&warn(start,{tag:soleTag(gained.map(tag=>({tag}))),member:name,code:NAME_GAINED_OWNER_REVIEW,target:name,message:`In ${release}, ${list(gained)} also ${gained.length===1?"dispatches":"dispatch"} ${name}. This ${context} does not prove which element it listens to, so it may start receiving those events; ignore them by event target if it should not.`}),unownedReviews("event",name,start,owner)},memberSite=({owner,kind,name,start,forms,dynamic,removal:removalRange})=>{const review=forms.map(([candidateKind,candidate])=>profile.reviewFor(owner,candidateKind,candidate)).find(Boolean);review&&reportReview(start,review);const rule=forms.map(([candidateKind,candidate])=>profile.renameFor(owner,candidateKind,candidate)).find(Boolean);if(!rule)return null;if(rule.polarity==="inverted"&&(dynamic||!removalRange)){const target=kind==="binding"?memberStyle(name,rule.to):rule.to;return warn(start,{tag:owner,member:rule.from,code:POLARITY_REVIEW,target:rule.to,message:`${target} is the inverse of ${name} on ${owner}, and this ${kind==="binding"?"binding":kind==="property"?"property access":"attribute binding"} is not a static attribute. Bind the negated value to ${target} by hand; ${name} keeps working until ${removal}.`}),null}return rule},renamedMembers=owner=>unique([...profile.data.renames.filter(entry=>entry.tag===owner&&["attribute","property","event"].includes(entry.kind)).map(entry=>entry.from),...profile.data.reviews.filter(entry=>entry.tag===owner&&["attribute","property","event"].includes(entry.kind)).map(entry=>entry.name)]),defaultTokens=[];for(const token of openingTokens){const owner=token.tag.toLowerCase(),attributes=parseTagAttributes(original,token),lyraOwner=profile.tags.has(owner),bound=lyraOwner?boundMemberNames(attributes):null,finalNames=new Map,planned=attributes.map(attribute=>{const event=markupEventName(attribute.rawName);let finalRaw=attribute.rawName;if(event&&/^lr-/.test(event.name)){const to=eventMove(event.name,owner);to&&(finalRaw=attribute.rawName.slice(0,event.offset)+to+attribute.rawName.slice(event.offset+event.name.length))}else if(lyraOwner){const binding=markupMemberBinding(attribute),rule=binding&&!binding.form.startsWith("member")&&profile.renameFor(owner,binding.form,binding.name);rule&&!rule.polarity&&(finalRaw=attribute.rawName.slice(0,binding.offset)+rule.to+attribute.rawName.slice(binding.offset+binding.name.length))}const key=finalRaw.toLowerCase();return finalNames.set(key,(finalNames.get(key)??0)+1),{attribute,event,key,renamed:finalRaw!==attribute.rawName}});if(lyraOwner&&renamedMembers(owner).length)for(const group of openingTagBraceGroups(original,token)){const shorthand=/^[A-Za-z_$][\w$]*$/.exec(group.body)?.[0];if(!shorthand){warn(group.start,{tag:owner,member:owner,code:"DYNAMIC_VALUE_REVIEW",target:null,message:`This spread on ${owner} may carry renamed or deprecated members (${list(renamedMembers(owner))}); rename them where the object is built.`});continue}const forms=[["property",camelCase(shorthand)],["attribute",kebabCase(shorthand)]],review=forms.map(([kind,name])=>profile.reviewFor(owner,kind,name)).find(Boolean);review&&reportReview(group.start+1,review);const rule=forms.map(([kind,name])=>profile.renameFor(owner,kind,name)).find(Boolean);rule&&warn(group.start+1,{tag:owner,member:rule.from,code:rule.polarity?POLARITY_REVIEW:RENAME_REVIEW,target:rule.to,message:`The shorthand {${shorthand}} binds the deprecated ${owner} ${rule.kind} ${rule.from}; write ${memberStyle(shorthand,rule.to)}={${rule.polarity?`!${shorthand}`:shorthand}} by hand.`})}for(const{attribute,event,key,renamed}of planned){if(event){/^lr-/.test(event.name)&&eventSite({name:event.name,start:attribute.nameStart+event.offset,owner,context:"listener",conflict:renamed&&finalNames.get(key)>1});continue}if(!lyraOwner)continue;if(/^v-(?:bind|on)$/.test(attribute.rawName)||attribute.rawName.startsWith("${")&&!/^\$\{\s*ref\s*\(/.test(attribute.rawName)){renamedMembers(owner).length&&warn(attribute.nameStart,{tag:owner,member:owner,code:"DYNAMIC_VALUE_REVIEW",target:null,message:`This object binding on ${owner} may carry renamed or deprecated members (${list(renamedMembers(owner))}); rename them where the object is built.`});continue}if(attribute.rawName==="exportparts"){if(attribute.valueKind!=="literal"||/\$\{|\{\{/.test(attribute.value??""))continue;for(const match of(attribute.value??"").matchAll(/([^,\s:]+)(\s*:\s*[^,\s]+)?/g)){const inner=match[1],start2=attribute.valueStart+match.index,review=profile.reviewFor(owner,"part",inner);review&&reportReview(start2,review);const rule2=profile.renameFor(owner,"part",inner);rule2&&rewrite(start2,start2+inner.length,match[2]?rule2.to:`${rule2.to}:${inner}`,{tag:owner,member:inner,action:"rewrite-part",target:rule2.to,message:match[2]?`Forward the renamed ${owner} part ${rule2.to} under the same exported name.`:`Forward the renamed ${owner} part ${rule2.to} under its previous exported name ${inner}.`})}continue}const binding=markupMemberBinding(attribute);if(!binding)continue;const forms=binding.form==="member"?[["property",camelCase(binding.name)],["attribute",kebabCase(binding.name)]]:[[binding.form,binding.name]],start=attribute.nameStart+binding.offset,staticAttribute=binding.form==="attribute"&&binding.offset===0&&!binding.dynamic,rule=memberSite({owner,kind:binding.form==="member"?"binding":binding.form,name:binding.name,start,forms,dynamic:binding.dynamic,removal:staticAttribute});if(!rule)continue;if(bound.has(kebabCase(rule.to))||renamed&&finalNames.get(key)>1){warn(start,{tag:owner,member:rule.from,code:RENAME_CONFLICT_REVIEW,target:rule.to,message:`This ${owner} already binds ${rule.to}; remove the deprecated ${rule.from} binding by hand.`});continue}if(rule.polarity==="inverted"){if(!attributeSemantics(token.start)){warn(start,{tag:owner,member:rule.from,code:POLARITY_REVIEW,target:rule.to,message:`${rule.to} is the inverse of ${rule.from}. In this template syntax a static value may be assigned to the ${camelCase(rule.from)} property as a string, where "false" is truthy, so set ${rule.to} by hand.`});continue}if(profile.defaultsFor(owner).some(entry=>entry.attribute===rule.to&&entry.value===!0)){warn(start,{tag:owner,member:rule.from,code:POLARITY_REVIEW,target:rule.to,message:`${rule.from} has a preserved absent default, so removing an explicit attribute would let a later run insert ${rule.to}. Review this value using the old converter (a presence boolean treats even "false" as true), then migrate it by hand after the default migration is complete.`});continue}const value=attribute.value??"",end=attributeEnd(original,attribute);if(value===""||value==="true"||value===rule.from){let removeFrom=attribute.nameStart;for(;removeFrom>0&&/\s/.test(original[removeFrom-1]);)removeFrom-=1;rewrite(removeFrom,end,"",{tag:owner,member:rule.from,action:"remove-attribute",target:rule.to,message:`Remove ${rule.from}: it restated the ${owner} default, and ${rule.to} is off by default.`})}else value==="false"?rewrite(attribute.nameStart,end,rule.to,{tag:owner,member:rule.from,action:"rewrite-attribute",target:rule.to,message:`Replace ${rule.from}="false" with the presence of ${rule.to}.`}):warn(start,{tag:owner,member:rule.from,code:POLARITY_REVIEW,target:rule.to,message:`${rule.from}="${attribute.value}" is not a boolean literal; set ${rule.to} to its inverse by hand.`});continue}const target=binding.form==="member"?memberStyle(binding.name,rule.to):rule.to;rewrite(start,start+binding.name.length,target,{tag:owner,member:rule.from,action:`rewrite-${rule.kind}`,target:rule.to,message:`Rename the ${owner} ${rule.kind} ${rule.from} to ${rule.to}.`})}lyraOwner&&profile.defaultsFor(owner).length&&defaultTokens.push({token,owner,bound})}const slotTags=[...profile.tags].filter(tag=>profile.slotContentFor(tag).length||[...profile.data.renames,...profile.data.reviews].some(entry=>entry.tag===tag&&entry.kind==="slot"));for(const tag of slotTags)for(const pair of elementPairs(original,tag,inComment))for(const child of directChildTokens(original,pair.opening.end+1,pair.closingStart,inComment)){let slot="",start=child.nameStart;for(const attribute of parseTagAttributes(original,child))if(attribute.rawName==="slot")slot=attribute.valueKind==="literal"?attribute.value??"":null,start=attribute.valueStart??attribute.nameStart;else if(attribute.rawName==="v-slot")slot="";else{const prefix=["v-slot:","#"].find(candidate=>attribute.rawName.startsWith(candidate));prefix&&(slot=attribute.rawName.slice(prefix.length),start=attribute.nameStart+prefix.length,slot==="default"&&(slot=""))}if(slot===null)continue;const review=profile.reviewFor(tag,"slot",slot);review&&reportReview(start,review);const rule=slot===""?null:profile.renameFor(tag,"slot",slot);if(rule&&rewrite(start,start+slot.length,rule.to,{tag,member:slot,action:"rewrite-slot",target:rule.to,message:`Rename the ${tag} slot ${slot} to ${rule.to}.`}),child.tag!=="template")for(const content of profile.slotContentFor(tag)){if(content.slot!==slot)continue;const listed=(content.report??content.allow).includes(child.tag);(content.report?!listed:listed)||warn(child.nameStart,{tag,member:child.tag,code:DEPRECATED_CONTENT_REVIEW,target:null,message:`<${child.tag}> in ${slot===""?"the default slot":`the ${slot} slot`} of ${tag}: ${content.summary}`})}}for(const match of original.matchAll(/::part\(([^)]*)\)/g)){if(inComment(match.index))continue;const owner=compoundTypeBefore(original,match.index),listStart=match.index+7;for(const nameMatch of match[1].matchAll(/\S+/g)){const name=nameMatch[0],start=listStart+nameMatch.index,own=owner?profile.renameFor(owner,"part",name):null;if(own)rewrite(start,start+name.length,own.to,{tag:owner,member:name,action:"rewrite-part",target:own.to,message:`Rename the ${owner} part ${name} to ${own.to}.`});else if(profile.renamesNamed("part",name).length&&!profile.exposes("part",name,owner)){const renames=profile.renamesNamed("part",name),keepers=profile.exposers("part",name).filter(tag=>!renames.some(entry=>entry.tag===tag));warn(start,{tag:soleTag(renames),member:name,code:RENAME_REVIEW,target:list(renames.map(entry=>entry.to)),message:`This ::part() selector does not name the component that owns the part. ${list(renames.map(entry=>entry.tag))} renamed the part ${name} to ${list(renames.map(entry=>entry.to))}`+(keepers.length?`, while ${list(keepers)} still ${keepers.length===1?"exposes":"expose"} ${name}`:"")+". Rename it where the selector reaches a renamed component directly; a part forwarded through exportparts keeps its exported name."})}else if(profile.gainedOwners("part",name).length&&!owner?.startsWith("lr-")){const gained=profile.gainedOwners("part",name);warn(start,{tag:soleTag(gained.map(tag=>({tag}))),member:name,code:NAME_GAINED_OWNER_REVIEW,target:name,message:`In ${release}, ${list(gained)} also ${gained.length===1?"exposes":"expose"} the part ${name}, so this selector may start matching it.`})}unownedReviews("part",name,start,owner)}}const cssNames=new Set([...profile.data.renames.filter(entry=>entry.kind==="css-property").flatMap(entry=>[entry.from,entry.to]),...profile.data.reviews.filter(entry=>entry.kind==="css-property").map(entry=>entry.name)]);if(cssNames.size)for(const match of original.matchAll(/(?<![\w-])--[A-Za-z0-9_-]+/g)){const name=match[0];if(!cssNames.has(name)||inComment(match.index))continue;profile.renamesNamed("css-property",name).length&&unownedMove({kind:"css-property",name,start:match.index,context:"custom property",rewritable:!0,action:"rewrite-css-property"});const declaration=/^\s*['"]?\s*:/.test(original.slice(match.index+name.length,match.index+name.length+16))||/setProperty\s*\(\s*['"`]$/.test(original.slice(Math.max(0,match.index-24),match.index)),gained=profile.gainedOwners("css-property",name);declaration&&gained.length&&warn(match.index,{tag:soleTag(gained.map(tag=>({tag}))),member:name,code:NAME_GAINED_OWNER_REVIEW,target:name,message:`In ${release}, ${list(gained)} also ${gained.length===1?"reads":"read"} ${name}, so this declaration now reaches them when they are nested below it.`});for(const review of profile.reviewsNamed("css-property",name))reportReview(match.index,review)}for(const match of original.matchAll(/\[\s*([A-Za-z_][\w-]*)(?=\s*(?:[~|^$*]?=|\]))/g)){if(inComment(match.index))continue;const owner=compoundTypeBefore(original,match.index);if(!owner||!profile.tags.has(owner))continue;const name=match[1],start=match.index+match[0].length-name.length,review=profile.reviewFor(owner,"attribute",name);review&&reportReview(start,review);const rule=profile.renameFor(owner,"attribute",name);if(rule){if(rule.polarity==="inverted"){warn(start,{tag:owner,member:name,code:POLARITY_REVIEW,target:rule.to,message:`${rule.to} is the inverse of ${name}; rewrite this ${owner} selector to test the opposite state (for example :not([${rule.to}])).`});continue}if(!rule.reflects){warn(start,{tag:owner,member:name,code:RENAME_REVIEW,target:rule.to,message:`${owner} does not reflect ${rule.to}, so [${rule.to}] matches only where markup sets it. Rename this selector once every place that sets ${name} (markup, setAttribute) sets ${rule.to} instead.`});continue}rewrite(start,start+name.length,rule.to,{tag:owner,member:name,action:"rewrite-attribute",target:rule.to,message:`Rename the ${owner} attribute selector [${name}] to [${rule.to}]; ${owner} reflects ${rule.to}.`})}}for(const match of original.matchAll(/:state\(\s*([a-z][a-z0-9-]*)\s*\)/g)){if(inComment(match.index))continue;const owner=compoundTypeBefore(original,match.index),review=owner?profile.reviewFor(owner,"css-state",match[1]):null;review&&reportReview(match.index+match[0].indexOf(match[1]),review)}for(const review of profile.data.reviews.filter(entry=>entry.kind==="component"))for(const match of original.matchAll(new RegExp(`(?<![\\w-])${review.tag}(?![\\w-])`,"g")))inComment(match.index)||original.slice(match.index-2,match.index)==="</"||reportReview(match.index,review);for(const match of original.matchAll(ANCHORED_MEMBER)){if(inComment(match.index))continue;const owner=match[2],member=match[3],start=match.index+match[0].length-member.length,after=start+member.length;if(handledMembers.add(start),LISTENER_METHODS.has(member)||ATTRIBUTE_METHODS.has(member)){const call=/^\s*\(\s*(['"`])([^'"`\n]+)\1/.exec(original.slice(after,after+200));if(!call)continue;const valueStart=after+call[0].length-1-call[2].length;if(handledStrings.add(valueStart-1),LISTENER_METHODS.has(member)){eventSite({name:call[2],start:valueStart,owner,context:"listener",conflict:sameHandlerListens(valueStart-1,call[2])&&"same-handler"});continue}const rule2=memberSite({owner,kind:"attribute",name:call[2],start:valueStart,forms:[["attribute",call[2]]],dynamic:!0});rule2&&rewrite(valueStart,valueStart+call[2].length,rule2.to,{tag:owner,member:rule2.from,action:"rewrite-attribute",target:rule2.to,message:`Rename the ${owner} attribute ${rule2.from} to ${rule2.to}.`});continue}if(!profile.tags.has(owner))continue;if(/^\s*\(/.test(original.slice(after,after+64))){const review=profile.reviewFor(owner,"method",member);review&&reportReview(start,review);continue}const rule=memberSite({owner,kind:"property",name:member,start,forms:[["property",member]],dynamic:!0});rule&&rewrite(start,after,rule.to,{tag:owner,member:rule.from,action:"rewrite-property",target:rule.to,message:`Rename the ${owner} property ${rule.from} to ${rule.to}.`})}const eventNameMatters=name=>profile.renamesNamed("event",name).length||profile.detailsNamed(name).length||profile.reviewsNamed("event",name).length||profile.gainedOwners("event",name).length;for(const match of original.matchAll(/(['"`])(?:(window|document|body):)?(lr-[a-z0-9]+(?:-[a-z0-9]+)*)\1/g)){const name=match[3];if(handledStrings.has(match.index)||inComment(match.index)||!eventNameMatters(name))continue;const before=original.slice(Math.max(0,match.index-120),match.index);if(!match[2]&&TAG_API_CALL_BEFORE.test(before))continue;const listener=LISTENER_CALL_BEFORE.test(before);eventSite({name,start:match.index+1+(match[2]?match[2].length+1:0),owner:null,context:listener?"listener":"event name string",rewritable:listener,conflict:listener&&!match[2]&&sameHandlerListens(match.index,name)&&"same-handler"})}const mentioned=mentionedTags(original,profile.tags,inComment),accessRules=new Map,addAccess=(name,candidate)=>accessRules.set(name,[...accessRules.get(name)??[],candidate]);for(const entry of profile.data.renames)entry.kind==="property"&&mentioned.has(entry.tag)&&addAccess(entry.from,{tag:entry.tag,rule:entry,call:!1});for(const entry of profile.data.reviews)(entry.kind==="property"||entry.kind==="method")&&mentioned.has(entry.tag)&&addAccess(entry.name,{tag:entry.tag,review:entry,call:entry.kind==="method"});if(accessRules.size){const inString=rangeTester(mergeRanges(quotedStringRanges(original,inComment))),inStylesheet=rangeTester(stylesheetRanges(original,file,inComment)),inToken=rangeTester(mergeRanges(openingTokens.map(token=>[token.start+1,token.end])));for(const match of original.matchAll(/(?<=[\w$)\]])\s*(?:\?\.|!?\.)\s*([A-Za-z_$][\w$]*)/g)){const name=match[1],candidates=accessRules.get(name);if(!candidates)continue;const start=match.index+match[0].length-name.length;if(handledMembers.has(start)||inComment(start)||inString(start)||inStylesheet(start)||inToken(start))continue;const call=/^\s*\(/.test(original.slice(start+name.length,start+name.length+64));for(const candidate of candidates){if(candidate.call!==call)continue;if(candidate.review){reportReview(start,candidate.review);continue}const{rule}=candidate;warn(start,{tag:rule.tag,member:name,code:rule.polarity?POLARITY_REVIEW:RENAME_REVIEW,target:rule.to,message:`If this reads or writes an ${rule.tag}, ${rule.polarity?`use the inverse property .${rule.to}`:`rename .${name} to .${rule.to}`}; .${name} keeps working until ${removal}.`})}}}for(const match of original.matchAll(/\.\s*(?:set|get|has|remove|toggle)Attribute\s*\(\s*(['"`])([a-z][a-z0-9-]*)\1/g)){const name=match[2],start=match.index+match[0].length-1-name.length;if(!(handledStrings.has(start-1)||inComment(match.index)))for(const tag of mentioned){const review=profile.reviewFor(tag,"attribute",name);review&&reportReview(start,review);const rule=profile.renameFor(tag,"attribute",name);rule&&warn(start,{tag,member:name,code:rule.polarity?POLARITY_REVIEW:RENAME_REVIEW,target:rule.to,message:`If this element is an ${tag}, ${rule.polarity?`use the inverse attribute ${rule.to}`:`rename ${name} to ${rule.to}`}; ${name} keeps working until ${removal}.`})}}if(profile.data.defaults.length){const defaultProfiles=new Map([...new Set(profile.data.defaults.map(entry=>entry.tag))].map(tag=>[tag,{origin,tag,defaults:profile.defaultsFor(tag).map(entry=>({member:entry.attribute,value:entry.value}))}])),hazards=scanLocalMigrationHazards(original,defaultProfiles,ignoredRanges,defaultTokens.map(({token})=>({...token,tag:token.tag.toLowerCase()}))),blocked=new Set([...options.blockedLocalMigrations??[],...hazards.blocked]);result.blockedLocalMigrations=hazards.blocked;for(const use of hazards.uses)warn(use.offset,{tag:use.tag,member:use.tag,code:use.warningCode,target:use.tag,message:use.message});for(const{token,owner,bound}of defaultTokens){const renamedAttributes=profile.data.renames.filter(entry=>entry.tag===owner&&entry.kind==="attribute"),missing=profile.defaultsFor(owner).filter(entry=>{const aliases=new Set([entry.attribute]);for(const rename of renamedAttributes)rename.from===entry.attribute&&aliases.add(rename.to),rename.to===entry.attribute&&aliases.add(rename.from);return![...aliases].some(name=>bound.has(name))});if(!missing.length)continue;if(blocked.has(localMigrationKey(origin,owner))){warn(token.nameStart,{tag:owner,member:owner,code:"MAPPING_REVIEW_BLOCKED",target:owner,message:`${owner} did not receive its Lyra ${profile.fromMajor} defaults (${missing.map(entry=>`${entry.attribute}=${String(entry.value)}`).join(", ")}) because the scanned target set accesses it through a DOM alias or an opaque attribute spread. Add them by hand where the previous default matters.`});continue}const insertions=[];for(const entry of missing)insertions.push(serializeLocalDefault({member:entry.attribute,value:entry.value})),result.changes.push(reportEntry({textStarts:starts,file,offset:token.end,origin,upstreamTag:owner,upstreamMember:entry.attribute,action:"insert-default",target:`${entry.attribute}=${String(entry.value)}`,message:`Insert ${entry.attribute} to preserve the Lyra ${profile.fromMajor} default.`}));if(insertions.length){const insertionOffset=original[token.end-1]==="/"?token.end-1:token.end,replacement=/\s/.test(original[insertionOffset-1])?`${insertions.join(" ")} `:` ${insertions.join(" ")}`;edits.push({start:insertionOffset,end:insertionOffset,replacement})}}}if(!profile.skipped.length)for(const{offset,entry}of acknowledgements.unused()){const key=`${offset}:${UNUSED_ACKNOWLEDGEMENT}:${entry.token}`;seenWarnings.has(key)||(seenWarnings.add(key),result.warnings.push(reportEntry({textStarts:starts,file,offset,origin,upstreamTag:null,upstreamMember:entry.token,action:"manual-review",target:null,warningCode:UNUSED_ACKNOWLEDGEMENT,message:entry.code?`The acknowledgement ${entry.token} matches no report on the lines it covers; remove it.`:`The acknowledgement ${entry.token} is not of the form CODE:name, for example DETAIL_SHAPE_REVIEW:lr-close.`})))}return result.content=finalizeEdits(original,edits),sortReportEntries(result.changes,"action"),sortReportEntries(result.warnings,"warningCode"),result}function scanUnrewrittenUpstreamReferences(text){const found=[],seen=new Set,push=(offset,upstreamTag,member,code,target,message)=>{const key=`${offset}:${code}`;seen.has(key)||(seen.add(key),found.push({offset,upstreamTag,member,code,target,message}))},TAG=/(?<![\w-])(?<tag>(?:wa|sl)-[a-z][a-z0-9-]*)(?![\w-])/g;for(const block of text.matchAll(/\bcss`/g)){const bodyStart=block.index+block[0].length,bodyEnd=text.indexOf("`",bodyStart);if(bodyEnd===-1)continue;const body=text.slice(bodyStart,bodyEnd);for(const match of body.matchAll(TAG)){const tag=match.groups.tag;push(bodyStart+match.index,tag,"css-template","CSS_IN_JS_SELECTOR_REVIEW",tag,`\`${tag}\` appears in a css\`\` template and was NOT rewritten. A rule keyed on a tag that no longer exists matches nothing, silently. Rename the selector by hand.`)}}for(const match of text.matchAll(/::slotted\(\s*([^)]*)\)/g))for(const inner of match[1].matchAll(TAG)){const tag=inner.groups.tag;push(match.index+match[0].indexOf(inner.groups.tag),tag,"::slotted","SLOTTED_SELECTOR_REVIEW",tag,`\`::slotted(${tag})\` was NOT rewritten and will match nothing after the migration. Rename it by hand.`)}const CALL=/\bthis(?:\s*\??\.\s*(?:shadowRoot|renderRoot))?\s*\??\.\s*(?:querySelector|querySelectorAll|closest|matches)\s*(?:<[^>\n]+>)?\(\s*(['"`])(?<selector>[^'"`]*)\1/g;for(const match of text.matchAll(CALL)){const selectorOffset=match.index+match[0].lastIndexOf(match.groups.selector);for(const inner of match.groups.selector.matchAll(TAG)){const tag=inner.groups.tag;push(selectorOffset+inner.index,tag,"selector-string","SELECTOR_STRING_REVIEW",tag,`\`${tag}\` appears in a DOM selector string that was NOT rewritten. The query will return null after the migration rather than throwing. Rename it by hand.`)}}const declaredHere=new Set([...text.matchAll(/(--(?:wa|sl)-[a-z0-9-]+)\s*:/g)].map(match=>match[1]));for(const match of text.matchAll(/--(?<ecosystem>wa|sl)-[a-z0-9-]+/g)){const token=match[0];declaredHere.has(token)||push(match.index,`${match.groups.ecosystem}-tokens`,"custom-property","UPSTREAM_TOKEN_REVIEW",token,`\`${token}\` is an upstream design token with no automatic Lyra equivalent, and was left as-is. A var() naming a token that no longer exists falls back to its second argument, or to nothing -- silently. Map it by hand against llms/tokens.md; note the spacing scales are offset by one step, so renaming by name alone tightens every gap.`)}return found}function migrateText(original,contract,options={}){if(options.origin&&contract.renameProfiles?.has(options.origin))return migrateRenameText(original,contract,options);if(options.origin)return migrateLocalText(original,contract,options);const file=options.file??"<memory>",rewriteBarePackages=options.rewriteBarePackages??new Set,rootRegistrationMappings=options.rootRegistrationMappings??new Map,starts=lineStarts(original),ignoredRanges=commentRanges(original),htmlStyleRanges=htmlTemplateStyleRanges(original),markupTokens=scanMarkupTags(original,ignoredRanges),allOpeningTokens=scanAllOpeningTags(original,ignoredRanges),apiReferences=scanApiTagReferences(original,ignoredRanges,options.domFactoryBindings),aliasReviews=scanAliasedRewriteReviews(original,contract,ignoredRanges),dynamicDefaultReviews=scanDynamicDefaultReviews(original,markupTokens,contract),conditionalBehaviorReviews=scanConditionalBehaviorReviews(original,markupTokens,ignoredRanges,contract),imports=scanImports(original,ignoredRanges,contract),bareImportPackages=new Set(imports.filter(imported=>imported.sideEffect&&!imported.subpath).map(imported=>imported.packageName)),blockedMappings=new Set([...options.blockedMappings??[],...aliasReviews.keys(),...dynamicDefaultReviews.keys()]),imperativeDefaultReviews=apiReferences.flatMap(reference=>{if(reference.kind!=="construction")return[];const mapping=contract.mappings.get(reference.tag),defaults=mapping?.rewrites.defaults.filter(rule=>rule.action==="insert-if-absent")??[];if(!mapping||!AUTO_CLASSIFICATIONS.has(mapping.classification)||defaults.length===0)return[];const assessment=assessImperativeDefaults(original,reference,defaults);return assessment.safe?[]:(blockedMappings.add(mapping.upstreamTag),[{reference,mapping,defaults,missing:assessment.missing}])}),registrationBlockedMappings=new Set(options.registrationBlockedMappings??[]),blockedEcosystems=new Set(options.blockedEcosystems??[]);for(const imported of imports){if(imported.sideEffect)continue;const upstreamTag=imported.subpath?deepImportTag(imported.specifier,imported.ecosystem):null,mapping=upstreamTag?contract.mappings.get(upstreamTag):null;mapping&&AUTO_CLASSIFICATIONS.has(mapping.classification)?blockedMappings.add(mapping.upstreamTag):imported.subpath||blockedEcosystems.add(imported.ecosystem)}const edits=[],changes=[],warnings=[],usage={webawesome:{automatic:0,manual:0},shoelace:{automatic:0,manual:0}},automaticMappings=new Set,deepRegistrationMappings=new Set,reportedOptionalPeers=new Set,reportOrigin=(upstreamTag,origin)=>origin??(upstreamTag?.startsWith("wa-")?"webawesome":upstreamTag?.startsWith("sl-")?"shoelace":null),change=(offset,upstreamTag,upstreamMember,action,target,message,origin=null)=>{changes.push(reportEntry({textStarts:starts,file,offset,origin:reportOrigin(upstreamTag,origin),upstreamTag,upstreamMember,action,target,message}))},warn=(offset,upstreamTag,upstreamMember,code,target,message,origin=null,behaviorReviewFlags=null)=>{warnings.push(reportEntry({textStarts:starts,file,offset,origin:reportOrigin(upstreamTag,origin),upstreamTag,upstreamMember,action:"manual-review",target,warningCode:code,behaviorReviewFlags,message}))},noteRuntimeRequirements=(mapping,offset)=>{for(const peer of mapping.target?.optionalPeers??[]){const key=`${mapping.targetTag}:${peer}`;reportedOptionalPeers.has(key)||(reportedOptionalPeers.add(key),warn(offset,mapping.upstreamTag,"runtime","OPTIONAL_PEER_REQUIRED",peer,`${mapping.targetTag} requires the optional peer package ${peer}; install a compatible version before relying on the migrated component.`))}},noteAutomatic=(mapping,offset)=>{automaticMappings.add(mapping.upstreamTag),noteRuntimeRequirements(mapping,offset)},addEdit=(start,end,replacement,details)=>{edits.push({start,end,replacement}),change(start,details.upstreamTag,details.upstreamMember,details.action,details.target,details.message,details.origin)},isBlocked=mapping=>!!mapping&&(blockedMappings.has(mapping.upstreamTag)||registrationBlockedMappings.has(mapping.upstreamTag)||blockedEcosystems.has(mapping.upstream)),isBlockedAutomatic=mapping=>AUTO_CLASSIFICATIONS.has(mapping?.classification)&&isBlocked(mapping),isAutomatic=mapping=>AUTO_CLASSIFICATIONS.has(mapping?.classification)&&!isBlocked(mapping),blockedMessage=mapping=>registrationBlockedMappings.has(mapping.upstreamTag)?`${mapping.upstreamTag} remains unchanged because the scanned target set does not prove registration for ${mapping.targetTag}. Include ${targetImport(mapping.target)} in the scan or migrate the file that owns its supported root registration import.`:`${mapping.upstreamTag} remains unchanged because the scanned target set contains a use that requires manual member, default, or import review.`,blockedWarningCode=mapping=>registrationBlockedMappings.has(mapping.upstreamTag)?"REGISTRATION_CLOSURE_REQUIRED":"MAPPING_REVIEW_BLOCKED";for(const{mapping,uses}of aliasReviews.values())for(const use of uses)warn(use.offset,mapping.upstreamTag,use.upstreamMember,"ALIASED_MEMBER_REVIEW",use.target,use.message);for(const{mapping,uses}of dynamicDefaultReviews.values())for(const use of uses)warn(use.offset,mapping.upstreamTag,use.upstreamMember,"DYNAMIC_VALUE_REVIEW",use.target,use.message);for(const{reference,mapping,defaults,missing}of imperativeDefaultReviews)warn(reference.start,mapping.upstreamTag,null,"IMPERATIVE_DEFAULT_REVIEW",mapping.targetTag,`${mapping.upstreamTag} is constructed imperatively and requires explicit migrated defaults (${defaults.map(rule=>`${rule.member}=${String(rule.value)}`).join(", ")}); review the created element before renaming this mapping. No unconditional immediate assignment was proven for ${missing.map(rule=>rule.member).join(", ")}.`);const openingTokens=[],manualOpeningTags=new Set;for(const token of markupTokens){const mapping=contract.mappings.get(token.tag),ecosystem=ecosystemForTag(token.tag);if(!isAutomatic(mapping)){if(mapping&¬eRuntimeRequirements(mapping,token.nameStart),token.closing&&manualOpeningTags.has(token.tag))continue;token.closing||manualOpeningTags.add(token.tag),usage[ecosystem].manual+=1,warn(token.nameStart,token.tag,null,isBlockedAutomatic(mapping)?blockedWarningCode(mapping):warningCode(mapping),mapping?.targetTag??null,isBlockedAutomatic(mapping)?blockedMessage(mapping):mappingMessage(mapping),null,mapping?.parity?.behaviorReviewFlags);continue}usage[ecosystem].automatic+=1,noteAutomatic(mapping,token.nameStart),addEdit(token.nameStart,token.nameEnd,mapping.targetTag,{upstreamTag:token.tag,upstreamMember:null,action:"rewrite-tag",target:mapping.targetTag,message:`Rename ${token.tag} to ${mapping.targetTag}.`});const behaviorReview=conditionalBehaviorReviews.get(token.start);behaviorReview&&warn(token.nameStart,mapping.upstreamTag,null,"BEHAVIOR_REVIEW_REQUIRED",mapping.targetTag,behaviorReview.message,null,behaviorReview.flags),!token.closing&&REDUCED_MOTION_REVIEW_TAGS.has(token.tag)&&warn(token.nameStart,mapping.upstreamTag,null,"BEHAVIOR_REVIEW_REQUIRED",mapping.targetTag,reducedMotionReviewMessage(mapping.upstreamTag),null,["reduced-motion-default"]),!token.closing&&ICON_VOCABULARY_REVIEW_TAGS.has(token.tag)&&warn(token.nameStart,mapping.upstreamTag,null,"BEHAVIOR_REVIEW_REQUIRED",mapping.targetTag,iconVocabularyReviewMessage(mapping.upstreamTag),null,["icon-name-vocabulary"]),token.closing||openingTokens.push({token,mapping,attributes:parseTagAttributes(original,token)})}for(const reference of apiReferences){const mapping=contract.mappings.get(reference.tag),ecosystem=ecosystemForTag(reference.tag);if(!isAutomatic(mapping)){mapping&¬eRuntimeRequirements(mapping,reference.start),usage[ecosystem].manual+=1,warn(reference.start,reference.tag,null,isBlockedAutomatic(mapping)?blockedWarningCode(mapping):warningCode(mapping),mapping?.targetTag??null,isBlockedAutomatic(mapping)?blockedMessage(mapping):mappingMessage(mapping),null,mapping?.parity?.behaviorReviewFlags);continue}usage[ecosystem].automatic+=1,noteAutomatic(mapping,reference.start),addEdit(reference.start,reference.end,mapping.targetTag,{upstreamTag:reference.tag,upstreamMember:null,action:"rewrite-tag",target:mapping.targetTag,message:`Rename ${reference.tag} to ${mapping.targetTag}.`}),REDUCED_MOTION_REVIEW_TAGS.has(reference.tag)&&warn(reference.start,mapping.upstreamTag,null,"BEHAVIOR_REVIEW_REQUIRED",mapping.targetTag,reducedMotionReviewMessage(mapping.upstreamTag),null,["reduced-motion-default"]),ICON_VOCABULARY_REVIEW_TAGS.has(reference.tag)&&warn(reference.start,mapping.upstreamTag,null,"BEHAVIOR_REVIEW_REQUIRED",mapping.targetTag,iconVocabularyReviewMessage(mapping.upstreamTag),null,["icon-name-vocabulary"])}for(const{token,mapping,attributes}of openingTokens){for(const attribute of attributes){const rule=namedRule(attribute.rawName,mapping);rule&&addEdit(attribute.nameStart,attribute.nameEnd,rule.replacement,{upstreamTag:mapping.upstreamTag,upstreamMember:rule.from,action:memberAction(rule.section),target:rule.to,message:`Rewrite ${rule.section} member ${rule.from} to ${rule.to}.`})}const insertions=[];for(const rule of mapping.rewrites.defaults){const variants=new Set([rule.member,kebabCase(rule.member),camelCase(rule.member)]),attribute=attributes.find(entry=>variants.has(entry.rawName.replace(/^[.?:]/,"")));if(rule.action==="insert-if-absent"){if(attribute)continue;insertions.push(`${kebabCase(rule.member)}="${String(rule.value).replaceAll("&","&").replaceAll('"',""")}"`),change(token.end,mapping.upstreamTag,rule.member,"insert-default",`${rule.member}=${String(rule.value)}`,`Insert ${rule.member} to preserve the upstream default.`);continue}!attribute||attribute.valueKind==="boolean"||(isDynamicAttribute(attribute)?(usage[mapping.upstream].manual+=1,warn(attribute.valueStart,mapping.upstreamTag,rule.member,"DYNAMIC_VALUE_REVIEW",String(rule.to),`The ${rule.member} value is dynamic; replace ${String(rule.from)} with ${String(rule.to)} at its source.`)):attribute.value===String(rule.from)&&addEdit(attribute.valueStart,attribute.valueEnd,String(rule.to),{upstreamTag:mapping.upstreamTag,upstreamMember:rule.member,action:"replace-default",target:String(rule.to),message:`Replace ${rule.member} value ${String(rule.from)} with ${String(rule.to)}.`}))}if(insertions.length){const insertionOffset=original[token.end-1]==="/"?token.end-1:token.end;edits.push({start:insertionOffset,end:insertionOffset,replacement:` ${insertions.join(" ")}`})}}const stack=[],pairs=[];for(const token of markupTokens)if(!token.closing&&!token.selfClosing)stack.push(token);else if(token.closing)for(let index=stack.length-1;index>=0;index-=1){if(stack[index].tag!==token.tag)continue;const[opening]=stack.splice(index,1);pairs.push({opening,closing:token});break}for(const{opening,closing}of pairs){const mapping=contract.mappings.get(opening.tag);if(!(!isAutomatic(mapping)||!mapping.rewrites.slots.length)){for(const descendant of allOpeningTokens)if(!(descendant.start<=opening.end||descendant.end>=closing.start))for(const attribute of parseTagAttributes(original,descendant))if(attribute.rawName==="slot"&&attribute.valueKind==="literal"){const rule=mapping.rewrites.slots.find(entry=>entry.from===attribute.value);rule&&addEdit(attribute.valueStart,attribute.valueEnd,rule.to,{upstreamTag:mapping.upstreamTag,upstreamMember:rule.from,action:"rewrite-slot",target:rule.to,message:`Rewrite slot ${rule.from} to ${rule.to}.`})}else{const prefix=attribute.rawName.startsWith("v-slot:")?"v-slot:":attribute.rawName.startsWith("#")?"#":null;if(!prefix)continue;const name=attribute.rawName.slice(prefix.length),rule=mapping.rewrites.slots.find(entry=>entry.from===name);rule&&addEdit(attribute.nameStart,attribute.nameEnd,`${prefix}${rule.to}`,{upstreamTag:mapping.upstreamTag,upstreamMember:rule.from,action:"rewrite-slot",target:rule.to,message:`Rewrite slot ${rule.from} to ${rule.to}.`})}}}const cssRule=/(?<selector>[^{}]+)\{(?<body>[^{}]*)\}/g;for(const match of original.matchAll(cssRule)){if(insideRanges(match.index,ignoredRanges))continue;const selectorStart=match.index,bodyStart=match.index+match[0].indexOf(match.groups.body);for(const mapping of contract.mappings.values()){const tagPattern=new RegExp(`(?<![a-z0-9-])${mapping.upstreamTag}(?![a-z0-9-])`,"g"),selectorMatches=[...match.groups.selector.matchAll(tagPattern)].filter(tagMatch=>{const offset=selectorStart+tagMatch.index;if(insideRanges(offset,ignoredRanges))return!1;const quoted=quotedRangeContaining(original,offset);return quoted?insideRanges(offset,htmlStyleRanges)&&!insideTemplateExpression(original,quoted[0],offset):!0});if(selectorMatches.length){if(!isAutomatic(mapping)){usage[mapping.upstream].manual+=selectorMatches.length;for(const tagMatch of selectorMatches){const offset=selectorStart+tagMatch.index;noteRuntimeRequirements(mapping,offset),warn(offset,mapping.upstreamTag,null,isBlockedAutomatic(mapping)?blockedWarningCode(mapping):warningCode(mapping),mapping.targetTag,isBlockedAutomatic(mapping)?blockedMessage(mapping):mappingMessage(mapping),null,mapping?.parity?.behaviorReviewFlags)}continue}for(const tagMatch of selectorMatches){const offset=selectorStart+tagMatch.index;if(noteAutomatic(mapping,offset),mapping.upstreamTag==="wa-random-content"){const flags=["host-layout","multi-item-layout"];warn(offset,mapping.upstreamTag,"selector","BEHAVIOR_REVIEW_REQUIRED",mapping.targetTag,"Review this selector after migration: Web Awesome lays candidates out through the host, while Lyra uses a block host and owns simultaneous-item flex layout inside its shadow base.",null,flags)}addEdit(offset,offset+mapping.upstreamTag.length,mapping.targetTag,{upstreamTag:mapping.upstreamTag,upstreamMember:null,action:"rewrite-tag",target:mapping.targetTag,message:`Rename ${mapping.upstreamTag} selector to ${mapping.targetTag}.`})}for(const rule of mapping.rewrites.parts){const pattern=new RegExp(`(?<=::part\\()${rule.from.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}(?=\\))`,"g");for(const partMatch of match.groups.selector.matchAll(pattern)){const offset=selectorStart+partMatch.index;insideRanges(offset,ignoredRanges)||addEdit(offset,offset+rule.from.length,rule.to,{upstreamTag:mapping.upstreamTag,upstreamMember:rule.from,action:"rewrite-part",target:rule.to,message:`Rewrite CSS part ${rule.from} to ${rule.to}.`})}}for(const rule of mapping.rewrites.cssProperties){const pattern=new RegExp(`${rule.from.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}(?![a-z0-9-])`,"g");for(const propertyMatch of match.groups.body.matchAll(pattern)){const offset=bodyStart+propertyMatch.index;insideRanges(offset,ignoredRanges)||addEdit(offset,offset+rule.from.length,rule.to,{upstreamTag:mapping.upstreamTag,upstreamMember:rule.from,action:"rewrite-css-property",target:rule.to,message:`Rewrite CSS custom property ${rule.from} to ${rule.to}.`})}}}}}for(const mapping of contract.mappings.values()){if(!isAutomatic(mapping))continue;const owner=`(?:document\\.)?querySelector(?:<[^>\\n]+>)?\\(\\s*(['"])${mapping.upstreamTag.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\1\\s*\\)`;for(const[section,suffix]of[["methods","(?=\\s*\\()"],["properties","(?=\\s*(?:=|\\.|\\?|;|$))"]])for(const rule of mapping.rewrites[section]){const escaped=rule.from.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),regex=new RegExp(`${owner}\\s*(?:\\?\\.|!\\s*\\.|\\.)\\s*(?<member>${escaped})${suffix}`,"g");for(const memberMatch of original.matchAll(regex)){if(insideRanges(memberMatch.index,ignoredRanges))continue;const relative=memberMatch[0].lastIndexOf(memberMatch.groups.member),offset=memberMatch.index+relative;addEdit(offset,offset+rule.from.length,rule.to,{upstreamTag:mapping.upstreamTag,upstreamMember:rule.from,action:memberAction(section),target:rule.to,message:`Rewrite ${section} member ${rule.from} to ${rule.to}.`})}}for(const rule of mapping.rewrites.events){const escaped=rule.from.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),regex=new RegExp(`${owner}\\s*(?:\\?\\.|!\\s*\\.|\\.)\\s*(?:add|remove)EventListener\\(\\s*(['"])(?<member>${escaped})\\2`,"g");for(const eventMatch of original.matchAll(regex)){if(insideRanges(eventMatch.index,ignoredRanges))continue;const relative=eventMatch[0].lastIndexOf(eventMatch.groups.member),offset=eventMatch.index+relative;addEdit(offset,offset+rule.from.length,rule.to,{upstreamTag:mapping.upstreamTag,upstreamMember:rule.from,action:"rewrite-event",target:rule.to,message:`Rewrite event ${rule.from} to ${rule.to}.`})}}}for(const imported of imports){const{ecosystem}=imported;if(!imported.sideEffect){usage[ecosystem].manual+=1,warn(imported.start,imported.subpath?deepImportTag(imported.specifier,ecosystem):null,"module","IMPORT_BINDING_REVIEW_REQUIRED",null,`The ${imported.kind} import has runtime/type bindings whose exported names cannot be inferred safely.`,ecosystem);continue}if(!imported.subpath){const canRewrite=rewriteBarePackages.has(ecosystem)||rewriteBarePackages.has(imported.packageName),registeredMappings=rootRegistrationMappings.get(imported.packageName)??rootRegistrationMappings.get(ecosystem)??new Set,closure=registrationClosure(contract,registeredMappings);if(canRewrite&&closure.length>0){addEdit(imported.start,imported.end,closure[0],{upstreamTag:null,origin:ecosystem,upstreamMember:"module",action:"rewrite-import",target:closure[0],message:`Rewrite ${imported.packageName} root registration import to its proven Lyra registration closure.`});const lineEnding=original.includes(`\r
|
|
16
19
|
`)?`\r
|
|
17
20
|
`:`
|
|
18
|
-
`;for(const specifier of closure.slice(1))edits.push({start:imported.statementEnd,end:imported.statementEnd,replacement:`${lineEnding}import ${imported.quote}${specifier}${imported.quote};`}),change(imported.start,null,"module","insert-registration",specifier,"Insert granular registration for a root-excluded Lyra target.",ecosystem)}else warn(imported.start,null,"module","PACKAGE_IMPORT_BLOCKED","@aceshooting/lyra-ui/all.js",`The ${ecosystem} package import remains because this scan contains manual uses or no proven registration closure.`,ecosystem);continue}const upstreamTag=deepImportTag(imported.specifier,ecosystem),mapping=upstreamTag?contract.mappings.get(upstreamTag):null;if(mapping&&!packageProvidesMapping(imported,mapping)){usage[ecosystem].manual+=1,noteRuntimeRequirements(mapping,imported.start),warn(imported.start,upstreamTag,"module","PACKAGE_TIER_MISMATCH",mapping.targetTag,`${imported.packageName} does not provide the ${mapping.source.tier} ${upstreamTag} registration entry; use a package identity that provides that tier before migrating it.`,ecosystem);continue}if(!upstreamTag||!isAutomatic(mapping)){usage[ecosystem].manual+=1,mapping&¬eRuntimeRequirements(mapping,imported.start),warn(imported.start,upstreamTag,"module",isBlockedAutomatic(mapping)?blockedWarningCode(mapping):mapping?warningCode(mapping):"UNRESOLVED_DEEP_IMPORT",mapping?.targetTag??null,isBlockedAutomatic(mapping)?blockedMessage(mapping):upstreamTag?mappingMessage(mapping):`No component registration entry can be derived from ${imported.specifier}.`,ecosystem,mapping?.parity?.behaviorReviewFlags);continue}usage[ecosystem].automatic+=1,noteAutomatic(mapping,imported.start),deepRegistrationMappings.add(mapping.upstreamTag);const target=targetImport(mapping.target);addEdit(imported.start,imported.end,target,{upstreamTag,upstreamMember:"module",action:"rewrite-import",target,message:`Rewrite the ${upstreamTag} registration entry to its inventory module.`})}const rewrittenRanges=edits.map(edit=>[edit.start,edit.end]),wasRewritten=offset=>rewrittenRanges.some(([start,end])=>offset>=start&&offset<end);for(const scan of scanUnrewrittenUpstreamReferences(original))wasRewritten(scan.offset)||warn(scan.offset,scan.upstreamTag,scan.member,scan.code,scan.target,scan.message);const content=finalizeEdits(original,edits);return changes.sort((left,right)=>left.line-right.line||left.column-right.column||left.action.localeCompare(right.action)),warnings.sort((left,right)=>left.line-right.line||left.column-right.column||left.warningCode.localeCompare(right.warningCode)),{content,changes,warnings,usage,blockedMappings,blockedEcosystems,bareImportPackages,automaticMappings,deepRegistrationMappings}}function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
`;for(const specifier of closure.slice(1))edits.push({start:imported.statementEnd,end:imported.statementEnd,replacement:`${lineEnding}import ${imported.quote}${specifier}${imported.quote};`}),change(imported.start,null,"module","insert-registration",specifier,"Insert granular registration for a root-excluded Lyra target.",ecosystem)}else warn(imported.start,null,"module","PACKAGE_IMPORT_BLOCKED","@aceshooting/lyra-ui/all.js",`The ${ecosystem} package import remains because this scan contains manual uses or no proven registration closure.`,ecosystem);continue}const upstreamTag=deepImportTag(imported.specifier,ecosystem),mapping=upstreamTag?contract.mappings.get(upstreamTag):null;if(mapping&&!packageProvidesMapping(imported,mapping)){usage[ecosystem].manual+=1,noteRuntimeRequirements(mapping,imported.start),warn(imported.start,upstreamTag,"module","PACKAGE_TIER_MISMATCH",mapping.targetTag,`${imported.packageName} does not provide the ${mapping.source.tier} ${upstreamTag} registration entry; use a package identity that provides that tier before migrating it.`,ecosystem);continue}if(!upstreamTag||!isAutomatic(mapping)){usage[ecosystem].manual+=1,mapping&¬eRuntimeRequirements(mapping,imported.start),warn(imported.start,upstreamTag,"module",isBlockedAutomatic(mapping)?blockedWarningCode(mapping):mapping?warningCode(mapping):"UNRESOLVED_DEEP_IMPORT",mapping?.targetTag??null,isBlockedAutomatic(mapping)?blockedMessage(mapping):upstreamTag?mappingMessage(mapping):`No component registration entry can be derived from ${imported.specifier}.`,ecosystem,mapping?.parity?.behaviorReviewFlags);continue}usage[ecosystem].automatic+=1,noteAutomatic(mapping,imported.start),deepRegistrationMappings.add(mapping.upstreamTag);const target=targetImport(mapping.target);addEdit(imported.start,imported.end,target,{upstreamTag,upstreamMember:"module",action:"rewrite-import",target,message:`Rewrite the ${upstreamTag} registration entry to its inventory module.`})}const rewrittenRanges=edits.map(edit=>[edit.start,edit.end]),wasRewritten=offset=>rewrittenRanges.some(([start,end])=>offset>=start&&offset<end);for(const scan of scanUnrewrittenUpstreamReferences(original))wasRewritten(scan.offset)||warn(scan.offset,scan.upstreamTag,scan.member,scan.code,scan.target,scan.message);const content=finalizeEdits(original,edits);return changes.sort((left,right)=>left.line-right.line||left.column-right.column||left.action.localeCompare(right.action)),warnings.sort((left,right)=>left.line-right.line||left.column-right.column||left.warningCode.localeCompare(right.warningCode)),{content,changes,warnings,usage,blockedMappings,blockedEcosystems,bareImportPackages,automaticMappings,deepRegistrationMappings}}function diffLines(text){const lines=text.split(`
|
|
22
|
+
`),eol=lines.at(-1)==="";return eol&&lines.pop(),lines.map((line,index)=>{const terminated=index<lines.length-1||eol;return{line,eol:terminated,key:terminated?line:`${line}\0`}})}function diffOperations(before,after){let prefix=0;for(;prefix<before.length&&prefix<after.length&&before[prefix].key===after[prefix].key;)prefix+=1;let suffix=0;for(;suffix<before.length-prefix&&suffix<after.length-prefix&&before[before.length-1-suffix].key===after[after.length-1-suffix].key;)suffix+=1;const a=before.slice(prefix,before.length-suffix),b=after.slice(prefix,after.length-suffix),goesDown=(frontier2,diagonal,depth)=>diagonal===-depth||diagonal!==depth&&frontier2.get(diagonal-1)<frontier2.get(diagonal+1),trace=[];let frontier=new Map([[1,0]]);search:for(let depth=0;depth<=a.length+b.length;depth+=1){trace.push(frontier);const next=new Map;for(let diagonal=-depth;diagonal<=depth;diagonal+=2){let x2=goesDown(frontier,diagonal,depth)?frontier.get(diagonal+1):frontier.get(diagonal-1)+1,y2=x2-diagonal;for(;x2<a.length&&y2<b.length&&a[x2].key===b[y2].key;)x2+=1,y2+=1;if(next.set(diagonal,x2),x2>=a.length&&y2>=b.length)break search}frontier=next}const middle=[];let x=a.length,y=b.length;for(let depth=trace.length-1;depth>=0;depth-=1){const previous=trace[depth],diagonal=x-y,previousDiagonal=goesDown(previous,diagonal,depth)?diagonal+1:diagonal-1,previousX=previous.get(previousDiagonal),previousY=previousX-previousDiagonal;for(;x>previousX&&y>previousY;)x-=1,y-=1,middle.push({type:" ",before:prefix+x,after:prefix+y});depth>0&&(x===previousX?middle.push({type:"+",after:prefix+y-1}):middle.push({type:"-",before:prefix+x-1})),x=previousX,y=previousY}middle.reverse();const operations=[];for(let index=0;index<prefix;index+=1)operations.push({type:" ",before:index,after:index});operations.push(...middle);for(let index=suffix;index>0;index-=1)operations.push({type:" ",before:before.length-index,after:after.length-index});return operations}function quoteDiffPath(file){if(/^[\x21-\x7e]+$/.test(file)&&!/["\\]/.test(file))return file;let quoted='"';for(const byte of Buffer.from(file,"utf8"))byte===34||byte===92?quoted+=`\\${String.fromCharCode(byte)}`:byte>=32&&byte<=126?quoted+=String.fromCharCode(byte):quoted+=`\\${byte.toString(8).padStart(3,"0")}`;return`${quoted}"`}function unifiedDiff(file,original,content,context=3){if(original===content)return"";const before=diffLines(original),after=diffLines(content),operations=diffOperations(before,after),beforeSeen=[0],afterSeen=[0];for(const operation of operations)beforeSeen.push(beforeSeen.at(-1)+(operation.type==="+"?0:1)),afterSeen.push(afterSeen.at(-1)+(operation.type==="-"?0:1));const hunks=[];operations.forEach((operation,index)=>{if(operation.type===" ")return;const last=hunks.at(-1);last&&index-last.end<=context*2+1?last.end=index:hunks.push({start:index,end:index})});const output=[`--- ${quoteDiffPath(`a/${file}`)}
|
|
23
|
+
`,`+++ ${quoteDiffPath(`b/${file}`)}
|
|
24
|
+
`];for(const hunk of hunks){const start=Math.max(0,hunk.start-context),end=Math.min(operations.length,hunk.end+context+1),beforeCount=beforeSeen[end]-beforeSeen[start],afterCount=afterSeen[end]-afterSeen[start],beforeStart=beforeSeen[start]+(beforeCount?1:0),afterStart=afterSeen[start]+(afterCount?1:0);output.push(`@@ -${beforeStart},${beforeCount} +${afterStart},${afterCount} @@
|
|
25
|
+
`);for(const operation of operations.slice(start,end)){const record=operation.type==="+"?after[operation.after]:before[operation.before];output.push(`${operation.type}${record.line}
|
|
26
|
+
`),record.eol||output.push(`\
|
|
27
|
+
`)}}return output.join("")}function reportPathName(file,cwd){return(path.relative(cwd,file)||path.basename(file)).split(path.sep).join("/")}function migrateFiles({files,inventory,dryRun=!1,reportPath=null,cwd=process.cwd(),origin=null,renameLedger=null,lyraVersion=null,collectDiff=!1}){if(collectDiff){const outside=files.filter(file=>{const relative=path.relative(cwd,file);return relative===".."||relative.startsWith(`..${path.sep}`)||path.isAbsolute(relative)});if(outside.length)throw new Error(`--diff needs every target inside the working directory; run it from a common parent of ${outside.join(", ")}.`)}const contract=buildMigrationContract(inventory,{renameLedger,lyraVersion}),originals=new Map(files.map(file=>[file,fs.readFileSync(file,"utf8")])),diffs=[],noteDiff=(file,original,content)=>{collectDiff&&content!==original&&diffs.push(unifiedDiff(reportPathName(file,cwd),original,content))},finish=report=>(reportPath&&fs.writeFileSync(reportPath,`${JSON.stringify(report,null,2)}
|
|
28
|
+
`,"utf8"),collectDiff?{...report,diff:diffs.join("")}:report);if(origin!==null){const renameProfile=contract.renameProfiles.has(origin);invariant(contract.localMigrations.has(origin)||renameProfile,`unknown local migration origin ${String(origin)}`);const blockedLocalMigrations=new Set,constructedEvents=new Set;for(const[file,original]of originals){const analysis=migrateText(original,contract,{file:reportPathName(file,cwd),origin});for(const key of analysis.blockedLocalMigrations)blockedLocalMigrations.add(key);for(const name of analysis.constructedEvents??[])constructedEvents.add(name)}const changes2=[],warnings2=[];let filesChanged2=0,acknowledged=0;for(const[file,original]of originals){const result=migrateText(original,contract,{file:reportPathName(file,cwd),origin,blockedLocalMigrations,constructedEvents});changes2.push(...result.changes),warnings2.push(...result.warnings),acknowledged+=result.acknowledged??0,result.content!==original&&(filesChanged2+=1,noteDiff(file,original,result.content),dryRun||fs.writeFileSync(file,result.content,"utf8"))}const sortEntries2=entries=>entries.sort((left,right)=>left.file.localeCompare(right.file)||left.line-right.line||left.column-right.column||left.action.localeCompare(right.action));sortEntries2(changes2),sortEntries2(warnings2);const summary={rewrites:changes2.length,warnings:warnings2.length};return renameProfile&&(summary.acknowledged=acknowledged,lyraVersion!==null&&(summary.skipped=contract.renameProfiles.get(origin).skipped.length)),finish({schemaVersion:MIGRATION_REPORT_SCHEMA_VERSION,origin,dryRun,filesScanned:files.length,filesChanged:filesChanged2,changes:changes2,warnings:warnings2,summary})}const domFactoryBindings=buildProjectDomFactoryBindings(originals),blockedMappings=new Set,blockedEcosystems=new Set,bareImportPackages=new Set,automaticMappings=new Set,deepRegistrationMappings=new Set,usage={webawesome:{automatic:0,manual:0},shoelace:{automatic:0,manual:0}};for(const[file,original]of originals){const analysis=migrateText(original,contract,{file:reportPathName(file,cwd),rewriteBarePackages:new Set,domFactoryBindings:domFactoryBindings.get(path.resolve(file))});for(const ecosystem of Object.keys(usage))usage[ecosystem].automatic+=analysis.usage[ecosystem].automatic,usage[ecosystem].manual+=analysis.usage[ecosystem].manual;for(const upstreamTag of analysis.blockedMappings)blockedMappings.add(upstreamTag);for(const ecosystem of analysis.blockedEcosystems)blockedEcosystems.add(ecosystem);for(const packageName of analysis.bareImportPackages)bareImportPackages.add(packageName);for(const upstreamTag of analysis.automaticMappings)automaticMappings.add(upstreamTag);for(const upstreamTag of analysis.deepRegistrationMappings)deepRegistrationMappings.add(upstreamTag)}for(const packageName of bareImportPackages){const identity=contract.packageIdentities.get(packageName);identity&&usage[identity.ecosystem].manual>0&&blockedEcosystems.add(identity.ecosystem)}const rewriteBarePackages=new Set([...bareImportPackages].filter(packageName=>{const identity=contract.packageIdentities.get(packageName);if(!identity)return!1;const counts=usage[identity.ecosystem];return counts.automatic>0&&counts.manual===0&&!blockedEcosystems.has(identity.ecosystem)})),rootRegistrationMappings=new Map([...bareImportPackages].map(packageName=>{const identity=contract.packageIdentities.get(packageName),relevant=new Set([...automaticMappings].filter(upstreamTag=>{const mapping=contract.mappings.get(upstreamTag);return!!(identity&&mapping?.upstream===identity.ecosystem&&(!mapping.source?.tier||identity.tiers.has(mapping.source.tier)))}));return[packageName,relevant]})),provenRegistrationMappings=new Set(deepRegistrationMappings);for(const packageName of rewriteBarePackages)for(const upstreamTag of rootRegistrationMappings.get(packageName)??[])provenRegistrationMappings.add(upstreamTag);const registrationBlockedMappings=new Set([...automaticMappings].filter(upstreamTag=>!provenRegistrationMappings.has(upstreamTag))),changes=[],warnings=[];let filesChanged=0;for(const[file,original]of originals){const result=migrateText(original,contract,{file:reportPathName(file,cwd),rewriteBarePackages,rootRegistrationMappings,blockedMappings,blockedEcosystems,registrationBlockedMappings,domFactoryBindings:domFactoryBindings.get(path.resolve(file))});changes.push(...result.changes),warnings.push(...result.warnings),result.content!==original&&(filesChanged+=1,noteDiff(file,original,result.content),dryRun||fs.writeFileSync(file,result.content,"utf8"))}const sortEntries=entries=>entries.sort((left,right)=>left.file.localeCompare(right.file)||left.line-right.line||left.column-right.column||left.action.localeCompare(right.action));return sortEntries(changes),sortEntries(warnings),finish({schemaVersion:MIGRATION_REPORT_SCHEMA_VERSION,origin:null,dryRun,filesScanned:files.length,filesChanged,changes,warnings,summary:{rewrites:changes.length,warnings:warnings.length}})}function parseArgs(argv){const options={check:!1,diff:!1,dryRun:!1,help:!1,extensions:DEFAULT_EXTENSIONS,lyraVersion:null,origin:null,report:null,targets:[]};let positional=!1;for(const argument of argv)if(!positional&&argument==="--")positional=!0;else if(!positional&&(argument==="--dry-run"||argument==="-n"))options.dryRun=!0;else if(!positional&&argument==="--check")options.check=!0,options.dryRun=!0;else if(!positional&&argument==="--diff")options.diff=!0,options.dryRun=!0;else if(!positional&&(argument==="--help"||argument==="-h"))options.help=!0;else if(!positional&&argument.startsWith("--ext="))options.extensions=new Set(argument.slice(6).split(",").map(extension=>extension.trim().replace(/^\./,"").toLowerCase()).filter(Boolean));else if(!positional&&argument.startsWith("--report=")){if(options.report=argument.slice(9),!options.report)throw new Error("--report requires a path")}else if(!positional&&argument.startsWith("--lyra-version=")){if(options.lyraVersion=argument.slice(15),compareVersions(options.lyraVersion,options.lyraVersion)!==0)throw new Error(`--lyra-version needs a version such as 22.0.0, got ${options.lyraVersion||"nothing"}`)}else if(!positional&&argument.startsWith("--origin=")){if(options.origin=argument.slice(9),!options.origin)throw new Error("--origin requires a value");if(!MIGRATION_ORIGINS.includes(options.origin))throw new Error(`Unknown migration origin: ${options.origin}`)}else{if(!positional&&argument.startsWith("-"))throw new Error(`Unknown option: ${argument}`);options.targets.push(argument)}return options}function printUsage(){console.log(`Usage: lyra-ui-migrate [--check] [--dry-run] [--diff] [--origin=${MIGRATION_ORIGINS.join("|")}] [--lyra-version=x.y.z] [--report=path] [--ext=html,ts,...] targets...
|
|
21
29
|
|
|
22
30
|
Only exact and fully rewritten inventory mappings change automatically. Conceptual, unsafe,
|
|
23
31
|
unsupported, unknown, and unresolved deep-import uses remain unchanged with source-located
|
|
24
32
|
warnings. The optional JSON report has a stable schema for CI and review tooling.
|
|
25
33
|
|
|
26
34
|
Without --origin, only Web Awesome and Shoelace migrations run. --origin=lyra-v7 performs the
|
|
27
|
-
opt-in Lyra defaults migration
|
|
35
|
+
opt-in Lyra defaults migration. --origin=lyra-v21 migrates deprecated Lyra 21 member names
|
|
36
|
+
(attributes, properties, events, parts, custom properties and slots) where the rewrite cannot
|
|
37
|
+
change what a site reaches, preserves changed defaults, and reports everything else, including
|
|
38
|
+
listeners of events whose detail changed. Neither profile rewrites tags or imports. Run a Lyra
|
|
39
|
+
profile with the CLI of the installed release, after upgrading.
|
|
28
40
|
|
|
29
|
-
--dry-run, -n
|
|
30
|
-
--check
|
|
31
|
-
--
|
|
32
|
-
--
|
|
33
|
-
--
|
|
34
|
-
--
|
|
41
|
+
--dry-run, -n report changes without writing source files
|
|
42
|
+
--check exit nonzero when rewrites or warnings remain; never write source files
|
|
43
|
+
--diff print a unified diff of the changes instead of writing source files
|
|
44
|
+
--origin=lyra-v7 insert explicit attributes that preserve changed Lyra v7 defaults
|
|
45
|
+
--origin=lyra-v21 migrate names and defaults that change from Lyra 21 to Lyra 22
|
|
46
|
+
--lyra-version=x.y.z apply only rename entries available in this release (default: the
|
|
47
|
+
@aceshooting/lyra-ui installed under the working directory, when found)
|
|
48
|
+
--report=path write the stable JSON migration report
|
|
49
|
+
--ext=a,b,c extensions scanned for directory targets
|
|
50
|
+
--help, -h show this message
|
|
51
|
+
|
|
52
|
+
A reviewed rename-profile warning is acknowledged by a "lyra-migrate-reviewed: CODE:name" comment
|
|
53
|
+
(for example DETAIL_SHAPE_REVIEW:lr-close) on the reported line, alone on the line above it, or
|
|
54
|
+
directly before the reported element's opening tag.`)}function detectInstalledLyraVersion(directory=process.cwd()){let current=path.resolve(directory);for(;;){const manifest=path.join(current,"node_modules","@aceshooting","lyra-ui","package.json");if(fs.existsSync(manifest))try{const version=JSON.parse(fs.readFileSync(manifest,"utf8")).version;return compareVersions(version,version)===0?version:null}catch{return null}const parent=path.dirname(current);if(parent===current)return null;current=parent}}function walkDir(directory,filter){const files=[];for(const entry of fs.readdirSync(directory,{withFileTypes:!0})){if(entry.name.startsWith("."))continue;const target=path.join(directory,entry.name);entry.isDirectory()?IGNORE_DIR_NAMES.has(entry.name)||files.push(...walkDir(target,filter)):entry.isFile()&&filter(target,entry.name)&&files.push(target)}return files}function globToRegExp(segments){let source="^";for(let index=0;index<segments.length;index+=1){const segment=segments[index];segment==="**"?source+="(?:.*/)?":(source+=segment.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,"[^/]*").replace(/\?/g,"[^/]"),index<segments.length-1&&(source+="/"))}return new RegExp(`${source}$`)}function expandGlob(pattern){const segments=pattern.split(path.sep).join("/").split("/");let split=0;for(;split<segments.length&&!/[*?]/.test(segments[split]);)split+=1;const base=path.resolve(segments.slice(0,split).join("/")||".");if(!fs.existsSync(base)||!fs.statSync(base).isDirectory())return[];const regex=globToRegExp(segments.slice(split));return walkDir(base,()=>!0).filter(file=>regex.test(path.relative(base,file).split(path.sep).join("/")))}function collectFiles(targets,extensions){const files=new Set;for(const target of targets){if(/[*?]/.test(target)){for(const file of expandGlob(target))files.add(file);continue}const resolved=path.resolve(target);if(!fs.existsSync(resolved)){console.error(`warning: path not found, skipping: ${target}`);continue}if(fs.statSync(resolved).isDirectory())for(const file of walkDir(resolved,(_file,name)=>extensions.has(path.extname(name).slice(1).toLowerCase())))files.add(file);else files.add(resolved)}return[...files].sort()}function run(argv){let options;try{options=parseArgs(argv)}catch(error){return console.error(error instanceof Error?error.message:error),1}if(options.help||!options.targets.length)return printUsage(),options.help?0:1;const files=collectFiles(options.targets,options.extensions);if(!files.length)return console.error("No files matched the given path(s)/pattern(s)."),1;try{const inventory=JSON.parse(fs.readFileSync(inventoryPath,"utf8")),renameOrigin=LYRA_RENAME_ORIGINS.includes(options.origin),lyraVersion=renameOrigin?options.lyraVersion??detectInstalledLyraVersion():null,report=migrateFiles({files,inventory,dryRun:options.dryRun,origin:options.origin,renameLedger:packagedRuntime?null:readRenameLedger(),lyraVersion,reportPath:options.report?path.resolve(options.report):null,collectDiff:options.diff}),log=options.diff?console.error:console.log;options.diff&&process.stdout.write(report.diff),renameOrigin&&log(lyraVersion===null?"No installed @aceshooting/lyra-ui found under the working directory; applying every entry of the profile.":`Applying entries available in @aceshooting/lyra-ui ${lyraVersion}`+(report.summary.skipped?`; ${report.summary.skipped} entr${report.summary.skipped===1?"y needs":"ies need"} a later release.`:"."));for(const entry of report.changes)log(`${entry.file}:${entry.line}:${entry.column} ${entry.action}: ${entry.message}`);for(const entry of report.warnings)log(`${entry.file}:${entry.line}:${entry.column} warning ${entry.warningCode}: ${entry.message}`);if(log(`${report.filesScanned} file(s) scanned, ${report.filesChanged} changed, ${report.summary.rewrites} rewrite(s), ${report.summary.warnings} warning(s)`+(Object.hasOwn(report.summary,"acknowledged")?`, ${report.summary.acknowledged} acknowledged.`:".")),options.dryRun&&report.filesChanged&&log("Dry run only -- no source files were written."),options.report&&log(`JSON report written to ${options.report}.`),options.check){const remaining=report.filesChanged>0||report.summary.warnings>0;return log(remaining?`Migration check failed: ${report.filesChanged} file(s) need changes and ${report.summary.warnings} warning(s) require review.`:"Migration check passed: no rewrites or warnings remain."),remaining?1:0}return 0}catch(error){return console.error(error instanceof Error?error.message:error),1}}function invokedAsCli(){const invoked=process.argv[1];if(!invoked)return!1;const here=fileURLToPath(import.meta.url);try{return fs.realpathSync(invoked)===fs.realpathSync(here)}catch{return path.resolve(invoked)===here}}invokedAsCli()&&(process.exitCode=run(process.argv.slice(2)));export{MIGRATION_ORIGINS,MIGRATION_REPORT_SCHEMA_VERSION,MIGRATION_RUNTIME_SCHEMA_VERSION,buildMigrationContract,buildMirrorMap,collectFiles,createMigrationRuntimeInventory,detectInstalledLyraVersion,migrateFiles,migrateText,parseArgs,readRenameLedger,run,scanUnrewrittenUpstreamReferences,unifiedDiff};
|