banhaten 0.1.3 → 0.2.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 +45 -0
- package/CONTRIBUTING.md +44 -0
- package/README.md +65 -13
- package/SECURITY.md +15 -0
- package/banhaten.config.example.json +0 -2
- package/docs/design-system/README.md +9 -0
- package/docs/design-system/adoption-quality-remediation.md +87 -0
- package/docs/design-system/agent-usage.md +13 -0
- package/docs/design-system/appearance-presets.md +6 -1
- package/docs/design-system/component-docs-pattern.md +81 -0
- package/docs/design-system/component-token-consistency-audit.md +13 -3
- package/docs/design-system/components/README.md +3 -2
- package/docs/design-system/components/accordion.md +16 -0
- package/docs/design-system/components/activity-feed.md +15 -1
- package/docs/design-system/components/alert-dialog.md +17 -0
- package/docs/design-system/components/alert.md +46 -14
- package/docs/design-system/components/aspect-ratio.md +15 -0
- package/docs/design-system/components/attribute.md +16 -0
- package/docs/design-system/components/autocomplete.md +19 -0
- package/docs/design-system/components/avatar.md +18 -0
- package/docs/design-system/components/badge.md +19 -0
- package/docs/design-system/components/banner.md +15 -1
- package/docs/design-system/components/breadcrumbs.md +14 -0
- package/docs/design-system/components/button-group.md +34 -0
- package/docs/design-system/components/button.md +18 -0
- package/docs/design-system/components/card.md +18 -1
- package/docs/design-system/components/carousel.md +20 -3
- package/docs/design-system/components/catalog-components.md +15 -1
- package/docs/design-system/components/chart.md +15 -0
- package/docs/design-system/components/checkbox.md +16 -0
- package/docs/design-system/components/collapsible.md +15 -0
- package/docs/design-system/components/command.md +15 -0
- package/docs/design-system/components/context-menu.md +15 -0
- package/docs/design-system/components/date-picker.md +21 -1
- package/docs/design-system/components/divider.md +16 -0
- package/docs/design-system/components/empty-state.md +17 -0
- package/docs/design-system/components/field.md +17 -0
- package/docs/design-system/components/file-upload.md +14 -0
- package/docs/design-system/components/hover-card.md +15 -0
- package/docs/design-system/components/input-group.md +17 -0
- package/docs/design-system/components/input-otp.md +18 -0
- package/docs/design-system/components/input.md +43 -7
- package/docs/design-system/components/item.md +72 -0
- package/docs/design-system/components/kbd.md +18 -0
- package/docs/design-system/components/label.md +16 -0
- package/docs/design-system/components/menu.md +17 -0
- package/docs/design-system/components/menubar.md +15 -0
- package/docs/design-system/components/modal.md +25 -4
- package/docs/design-system/components/native-select.md +18 -0
- package/docs/design-system/components/navigation-menu.md +15 -0
- package/docs/design-system/components/onboarding-step-list-item.md +15 -1
- package/docs/design-system/components/page-header.md +15 -1
- package/docs/design-system/components/pagination.md +30 -8
- package/docs/design-system/components/popover.md +19 -0
- package/docs/design-system/components/progress-slider.md +18 -0
- package/docs/design-system/components/progress.md +17 -0
- package/docs/design-system/components/radio-card.md +16 -0
- package/docs/design-system/components/radio-group.md +17 -0
- package/docs/design-system/components/resizable.md +15 -0
- package/docs/design-system/components/scroll-area.md +15 -0
- package/docs/design-system/components/segmented-control.md +71 -0
- package/docs/design-system/components/select.md +44 -7
- package/docs/design-system/components/sheet.md +17 -0
- package/docs/design-system/components/sidebar.md +15 -0
- package/docs/design-system/components/skeleton.md +18 -0
- package/docs/design-system/components/slideout.md +14 -0
- package/docs/design-system/components/slider.md +20 -0
- package/docs/design-system/components/social-button.md +21 -3
- package/docs/design-system/components/spinner.md +16 -0
- package/docs/design-system/components/steps.md +14 -0
- package/docs/design-system/components/table.md +15 -1
- package/docs/design-system/components/tabs.md +17 -0
- package/docs/design-system/components/tag.md +17 -0
- package/docs/design-system/components/textarea.md +16 -0
- package/docs/design-system/components/timeline.md +14 -0
- package/docs/design-system/components/toast.md +17 -0
- package/docs/design-system/components/toggle.md +17 -0
- package/docs/design-system/components/toolbar.md +21 -1
- package/docs/design-system/components/tooltip.md +18 -1
- package/docs/design-system/density-and-elevation.md +44 -0
- package/docs/design-system/direction-and-localization.md +23 -0
- package/docs/design-system/form-recipes.md +73 -0
- package/docs/design-system/gap-promotion-policy.md +21 -0
- package/docs/design-system/gap-promotion-report.md +166 -0
- package/docs/design-system/installation.md +68 -0
- package/docs/design-system/launch-comparison-shadcn.md +98 -0
- package/docs/design-system/registry-and-cli.md +30 -7
- package/docs/design-system/release-policy.md +30 -0
- package/docs/design-system/roadmap.md +14 -0
- package/docs/design-system/support-matrix.md +23 -0
- package/docs/design-system/tabs.md +1 -1
- package/docs/design-system/token-governance.md +23 -1
- package/package.json +29 -15
- package/registry/component-contract.json +40 -0
- package/registry/components/alert-dialog.tsx +1 -1
- package/registry/components/alert.tsx +86 -65
- package/registry/components/autocomplete.tsx +23 -9
- package/registry/components/avatar.tsx +7 -7
- package/registry/components/badge.tsx +1 -1
- package/registry/components/button-group.tsx +321 -4
- package/registry/components/button.tsx +38 -17
- package/registry/components/card.tsx +68 -59
- package/registry/components/carousel.tsx +53 -5
- package/registry/components/chart.tsx +36 -3
- package/registry/components/checkbox.tsx +3 -3
- package/registry/components/collapsible.tsx +1 -1
- package/registry/components/command.tsx +1 -1
- package/registry/components/context-menu.tsx +1 -1
- package/registry/components/date-picker.tsx +245 -104
- package/registry/components/expanded/ActivityFeed.tsx +6 -15
- package/registry/components/expanded/Banner.tsx +3 -3
- package/registry/components/expanded/Breadcrumbs.tsx +5 -5
- package/registry/components/expanded/CatalogComponentsShowcase.tsx +13 -13
- package/registry/components/expanded/CatalogDivider.tsx +1 -1
- package/registry/components/expanded/EmptyState.tsx +1 -1
- package/registry/components/expanded/FileUpload.tsx +9 -2
- package/registry/components/expanded/OnboardingStepListItem.tsx +2 -4
- package/registry/components/expanded/PageHeader.tsx +13 -13
- package/registry/components/expanded/Slideout.tsx +15 -415
- package/registry/components/expanded/Steps.tsx +4 -4
- package/registry/components/expanded/Table.tsx +116 -69
- package/registry/components/expanded/Timeline.tsx +3 -3
- package/registry/components/expanded/activityFeed.css +1 -1
- package/registry/components/expanded/breadcrumbs.css +1 -1
- package/registry/components/expanded/divider.css +1 -1
- package/registry/components/expanded/emptyState.css +1 -1
- package/registry/components/expanded/fileUpload.css +6 -6
- package/registry/components/expanded/slideout.css +9 -685
- package/registry/components/expanded/steps.css +3 -3
- package/registry/components/expanded/table.css +2 -2
- package/registry/components/expanded/timeline.css +2 -2
- package/registry/components/input-content.ts +1 -0
- package/registry/components/input-otp.tsx +13 -3
- package/registry/components/input.tsx +68 -43
- package/registry/components/item.tsx +189 -0
- package/registry/components/kbd.tsx +111 -4
- package/registry/components/menu.tsx +31 -245
- package/registry/components/menubar.tsx +1 -1
- package/registry/components/modal.tsx +142 -102
- package/registry/components/native-select.tsx +2 -2
- package/registry/components/pagination.tsx +80 -58
- package/registry/components/popover.tsx +162 -664
- package/registry/components/radio-card.tsx +116 -87
- package/registry/components/segmented-control.tsx +87 -0
- package/registry/components/select-content.tsx +2 -2
- package/registry/components/select.tsx +52 -16
- package/registry/components/sheet.tsx +1 -1
- package/registry/components/sidebar.tsx +1 -1
- package/registry/components/skeleton.css +0 -3
- package/registry/components/social-button.tsx +47 -25
- package/registry/components/spinner.tsx +18 -55
- package/registry/components/tabs.css +1 -1
- package/registry/components/tabs.tsx +8 -4
- package/registry/components/tag.tsx +2 -2
- package/registry/components/textarea.tsx +3 -3
- package/registry/components/toast.tsx +16 -11
- package/registry/components/toggle.tsx +2 -2
- package/registry/components/toolbar.tsx +89 -17
- package/registry/components/tooltip.tsx +1 -1
- package/registry/examples/alert-demo.tsx +25 -5
- package/registry/examples/attribute-demo.tsx +1 -1
- package/registry/examples/autocomplete-demo.tsx +3 -3
- package/registry/examples/avatar-demo.tsx +7 -7
- package/registry/examples/badge-demo.tsx +1 -1
- package/registry/examples/button-demo.tsx +6 -6
- package/registry/examples/button-group-demo.tsx +23 -2
- package/registry/examples/card-demo.tsx +46 -8
- package/registry/examples/carousel-demo.tsx +20 -2
- package/registry/examples/chart-demo.tsx +1 -1
- package/registry/examples/checkbox-demo.tsx +5 -5
- package/registry/examples/collection-filter-bar-demo.tsx +135 -0
- package/registry/examples/command-demo.tsx +3 -3
- package/registry/examples/{expanded/command-bar-demo.tsx → command-palette-demo.tsx} +5 -15
- package/registry/examples/context-menu-demo.tsx +1 -1
- package/registry/examples/control-density-contract-demo.tsx +61 -0
- package/registry/examples/date-picker-demo.tsx +12 -12
- package/registry/examples/divider-demo.tsx +3 -3
- package/registry/examples/expanded/banner-demo.tsx +1 -1
- package/registry/examples/expanded/breadcrumbs-demo.tsx +1 -1
- package/registry/examples/expanded/empty-state-demo.tsx +1 -1
- package/registry/examples/expanded/file-upload-demo.tsx +1 -1
- package/registry/examples/expanded/onboarding-step-demo.tsx +1 -1
- package/registry/examples/expanded/slideout-demo.tsx +2 -2
- package/registry/examples/expanded/steps-demo.tsx +1 -1
- package/registry/examples/expanded/tabs-demo.tsx +1 -1
- package/registry/examples/expanded/timeline-demo.tsx +1 -1
- package/registry/examples/hover-card-demo.tsx +2 -2
- package/registry/examples/input-demo.tsx +6 -6
- package/registry/examples/input-otp-demo.tsx +4 -4
- package/registry/examples/item-demo.tsx +46 -0
- package/registry/examples/kbd-demo.tsx +1 -1
- package/registry/examples/label-demo.tsx +1 -1
- package/registry/examples/menu-demo.tsx +1 -1
- package/registry/examples/modal-demo.tsx +67 -9
- package/registry/examples/navigation-menu-demo.tsx +1 -1
- package/registry/examples/pagination-demo.tsx +6 -3
- package/registry/examples/popover-demo.tsx +19 -19
- package/registry/examples/progress-demo.tsx +1 -1
- package/registry/examples/progress-slider-demo.tsx +1 -1
- package/registry/examples/radio-card-demo.tsx +4 -4
- package/registry/examples/radio-group-demo.tsx +2 -2
- package/registry/examples/resizable-demo.tsx +1 -1
- package/registry/examples/results-toolbar-demo.tsx +44 -0
- package/registry/examples/scroll-area-demo.tsx +3 -3
- package/registry/examples/segmented-control-demo.tsx +39 -0
- package/registry/examples/select-demo.tsx +1 -1
- package/registry/examples/sidebar-demo.tsx +1 -1
- package/registry/examples/slider-demo.tsx +3 -3
- package/registry/examples/social-button-demo.tsx +18 -8
- package/registry/examples/spinner-demo.tsx +13 -13
- package/registry/examples/tabs-demo.tsx +1 -1
- package/registry/examples/tag-demo.tsx +1 -1
- package/registry/examples/textarea-demo.tsx +4 -4
- package/registry/examples/toast-demo.tsx +1 -1
- package/registry/examples/toggle-demo.tsx +6 -6
- package/registry/examples/tooltip-demo.tsx +4 -4
- package/registry/guidance/USAGE.md +27 -0
- package/registry/hooks/use-direction.ts +193 -13
- package/registry/index.json +5311 -377
- package/registry/styles/globals.css +2440 -4061
- package/registry.json +119 -149
- package/schema/config.schema.json +11 -8
- package/schema/registry.schema.json +150 -45
- package/schema/tokens.schema.json +134 -34
- package/src/cli/command-line.js +194 -0
- package/src/cli/config-editors.js +9 -0
- package/src/cli/doctor.js +399 -0
- package/src/cli/file-transaction.js +295 -0
- package/src/cli/global-css.js +153 -0
- package/src/cli/index.js +567 -1613
- package/src/cli/install-plan.js +592 -0
- package/src/cli/install-receipt.js +171 -0
- package/src/cli/jsonc.js +203 -0
- package/src/cli/package-install-state.js +254 -0
- package/src/cli/package-manager.js +238 -0
- package/src/cli/package-plan.js +185 -0
- package/src/cli/package-version.js +97 -0
- package/src/cli/registry-query.js +207 -0
- package/src/cli/reporting.js +55 -0
- package/src/cli/schema-validation.js +19 -0
- package/src/cli/source-scanner.js +686 -0
- package/src/cli/vite-config.js +478 -0
- package/tokens/banhaten.tokens.json +4312 -309
- package/docs/design-system/components/command-bar.md +0 -57
- package/registry/components/expanded/BannerBoard.tsx +0 -225
- package/registry/components/expanded/CatalogTag.tsx +0 -85
- package/registry/components/expanded/CommandBar.tsx +0 -386
- package/registry/components/expanded/IconExplorer.tsx +0 -612
- package/registry/components/expanded/TimelineShowcase.tsx +0 -158
- package/registry/components/expanded/commandBar.css +0 -470
- package/registry/components/expanded/iconExplorer.css +0 -764
- package/registry/components/expanded/iconPacks.ts +0 -866
- package/registry/components/expanded/tag.css +0 -148
- package/registry/examples/expanded/icons-demo.tsx +0 -5
- package/tokens/Color mode.zip +0 -0
- package/tokens/Numbers.zip +0 -0
- package/tokens/Radius.zip +0 -0
- package/tokens/Theme.zip +0 -0
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
// The scanner reports syntax problems without naming a dialect; this module is
|
|
2
|
+
// the only place that knows the source is a Vite config, so it adds that word.
|
|
3
|
+
function asViteConfigError(error) {
|
|
4
|
+
if (error instanceof Error && !/^unsupported Vite config: /.test(error.message)) {
|
|
5
|
+
error.message = `unsupported Vite config: ${error.message}`
|
|
6
|
+
}
|
|
7
|
+
return error
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Vite config editing. This module owns every assumption about what a Vite
|
|
11
|
+
// config looks like; the scanner primitives it builds on know nothing about Vite.
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
appendArrayElement,
|
|
15
|
+
appendObjectProperty,
|
|
16
|
+
arrayHasCallExpression,
|
|
17
|
+
createBracketRange,
|
|
18
|
+
expectArrayLiteralValue,
|
|
19
|
+
expectObjectLiteralValue,
|
|
20
|
+
findExportDefaultExpressionStart,
|
|
21
|
+
findObjectProperty,
|
|
22
|
+
findTopLevelCommaOrEnd,
|
|
23
|
+
insertObjectProperty,
|
|
24
|
+
parseImportDeclarations,
|
|
25
|
+
prependImportLines,
|
|
26
|
+
readIdentifierAt,
|
|
27
|
+
readObjectPropertyNames,
|
|
28
|
+
readPropertyName,
|
|
29
|
+
readStringLiteral,
|
|
30
|
+
skipWhitespaceAndComments,
|
|
31
|
+
skipWhitespaceCommentsAndCommas
|
|
32
|
+
} from "./source-scanner.js"
|
|
33
|
+
|
|
34
|
+
function ensureNodeUrlImport(source) {
|
|
35
|
+
const imports = parseImportDeclarations(source)
|
|
36
|
+
const nodeUrlImports = imports.filter((entry) => entry.moduleSpecifier === "node:url")
|
|
37
|
+
const importedLocals = new Set(nodeUrlImports.flatMap((entry) => entry.namedLocals))
|
|
38
|
+
const missing = ["fileURLToPath", "URL"].filter((name) => !importedLocals.has(name))
|
|
39
|
+
|
|
40
|
+
if (missing.length === 0) return source
|
|
41
|
+
|
|
42
|
+
return prependImportLines(
|
|
43
|
+
source,
|
|
44
|
+
[`import { ${missing.join(", ")} } from "node:url"`]
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function ensureTailwindViteImport(source, localName) {
|
|
49
|
+
const imports = parseImportDeclarations(source)
|
|
50
|
+
const tailwindImport = imports.find(
|
|
51
|
+
(entry) => entry.moduleSpecifier === "@tailwindcss/vite"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if (!tailwindImport) {
|
|
55
|
+
return prependImportLines(
|
|
56
|
+
source,
|
|
57
|
+
[`import ${localName} from "@tailwindcss/vite"`]
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!tailwindImport.defaultLocal) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
"unsupported Vite config: @tailwindcss/vite must be imported with a default binding."
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return source
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function updateViteConfigSource(source, options) {
|
|
71
|
+
try {
|
|
72
|
+
return updateViteConfigSourceUnchecked(source, options)
|
|
73
|
+
} catch (error) {
|
|
74
|
+
throw asViteConfigError(error)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function updateViteConfigSourceUnchecked(source, { aliasRoot, sourceRoot }) {
|
|
79
|
+
validateViteConfigStructure(source)
|
|
80
|
+
const tailwindLocalName = getTailwindViteLocalName(source)
|
|
81
|
+
let next = aliasRoot
|
|
82
|
+
? addViteResolveAlias(source, aliasRoot, sourceRoot)
|
|
83
|
+
: source
|
|
84
|
+
const addedFileUrlAlias = Boolean(aliasRoot) && next !== source
|
|
85
|
+
next = addVitePluginCall(next, `${tailwindLocalName}()`, tailwindLocalName)
|
|
86
|
+
next = ensureTailwindViteImport(next, tailwindLocalName)
|
|
87
|
+
if (addedFileUrlAlias) next = ensureNodeUrlImport(next)
|
|
88
|
+
return next
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function readViteAliasSourceRoot(source, options) {
|
|
92
|
+
try {
|
|
93
|
+
return readViteAliasSourceRootUnchecked(source, options)
|
|
94
|
+
} catch (error) {
|
|
95
|
+
throw asViteConfigError(error)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function readViteAliasSourceRootUnchecked(source, { aliasRoot }) {
|
|
100
|
+
validateViteConfigStructure(source)
|
|
101
|
+
const configObject = findViteConfigObject(source)
|
|
102
|
+
const resolveProperty = findObjectProperty(source, configObject, "resolve")
|
|
103
|
+
if (!resolveProperty) return null
|
|
104
|
+
|
|
105
|
+
const resolveObject = expectObjectLiteralValue(
|
|
106
|
+
source,
|
|
107
|
+
resolveProperty,
|
|
108
|
+
"unsupported Vite config: resolve must be an object literal."
|
|
109
|
+
)
|
|
110
|
+
const aliasProperty = findObjectProperty(source, resolveObject, "alias")
|
|
111
|
+
if (!aliasProperty) return null
|
|
112
|
+
|
|
113
|
+
const aliasObject = expectObjectLiteralValue(
|
|
114
|
+
source,
|
|
115
|
+
aliasProperty,
|
|
116
|
+
"unsupported Vite config: resolve.alias must be an object literal."
|
|
117
|
+
)
|
|
118
|
+
const rootProperty = findObjectProperty(source, aliasObject, aliasRoot)
|
|
119
|
+
if (!rootProperty) return null
|
|
120
|
+
|
|
121
|
+
const expression = source
|
|
122
|
+
.slice(rootProperty.valueStart, rootProperty.valueEnd)
|
|
123
|
+
.trim()
|
|
124
|
+
const staticRoot = readStaticAliasRoot(expression)
|
|
125
|
+
if (staticRoot !== null) {
|
|
126
|
+
assertStaticAliasBinding(source, staticRoot)
|
|
127
|
+
return staticRoot
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
throw new Error(
|
|
131
|
+
`unsupported Vite config: resolve.alias[${JSON.stringify(aliasRoot)}] must use a static string, ` +
|
|
132
|
+
"fileURLToPath(new URL(..., import.meta.url)), or path.resolve(__dirname, ...)."
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function readViteAliasKeys(source) {
|
|
137
|
+
try {
|
|
138
|
+
return readViteAliasKeysUnchecked(source)
|
|
139
|
+
} catch (error) {
|
|
140
|
+
throw asViteConfigError(error)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function readViteAliasKeysUnchecked(source) {
|
|
145
|
+
validateViteConfigStructure(source)
|
|
146
|
+
const configObject = findViteConfigObject(source)
|
|
147
|
+
const resolveProperty = findObjectProperty(source, configObject, "resolve")
|
|
148
|
+
if (!resolveProperty) return []
|
|
149
|
+
|
|
150
|
+
const resolveObject = expectObjectLiteralValue(
|
|
151
|
+
source,
|
|
152
|
+
resolveProperty,
|
|
153
|
+
"unsupported Vite config: resolve must be an object literal."
|
|
154
|
+
)
|
|
155
|
+
const aliasProperty = findObjectProperty(source, resolveObject, "alias")
|
|
156
|
+
if (!aliasProperty) return []
|
|
157
|
+
|
|
158
|
+
const aliasObject = expectObjectLiteralValue(
|
|
159
|
+
source,
|
|
160
|
+
aliasProperty,
|
|
161
|
+
"unsupported Vite config: resolve.alias must be an object literal."
|
|
162
|
+
)
|
|
163
|
+
return readObjectPropertyNames(source, aliasObject)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function readStaticAliasRoot(expression) {
|
|
167
|
+
const directLiteral = readStringLiteral(expression, 0)
|
|
168
|
+
if (
|
|
169
|
+
directLiteral &&
|
|
170
|
+
skipWhitespaceAndComments(expression, directLiteral.end) === expression.length
|
|
171
|
+
) {
|
|
172
|
+
return { kind: "literal", value: directLiteral.value }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const patterns = [
|
|
176
|
+
{
|
|
177
|
+
kind: "file-url",
|
|
178
|
+
pattern: /^fileURLToPath\s*\(\s*new\s+URL\s*\(\s*(["'])(.*?)\1\s*,\s*import\.meta\.url\s*\)\s*\)$/s,
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
kind: "path-default",
|
|
182
|
+
pattern: /^path\.resolve\s*\(\s*(?:__dirname|import\.meta\.dirname)\s*,\s*(["'])(.*?)\1\s*\)$/s,
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
kind: "path-named",
|
|
186
|
+
pattern: /^resolve\s*\(\s*(?:__dirname|import\.meta\.dirname)\s*,\s*(["'])(.*?)\1\s*\)$/s,
|
|
187
|
+
},
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
for (const candidate of patterns) {
|
|
191
|
+
const match = expression.match(candidate.pattern)
|
|
192
|
+
if (match) {
|
|
193
|
+
return {
|
|
194
|
+
kind: candidate.kind,
|
|
195
|
+
value: match[2].replace(/\\(.)/g, "$1"),
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return null
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function assertStaticAliasBinding(source, staticRoot) {
|
|
204
|
+
const imports = parseImportDeclarations(source)
|
|
205
|
+
|
|
206
|
+
if (staticRoot.kind === "file-url") {
|
|
207
|
+
const nodeUrlImport = imports.find(
|
|
208
|
+
(entry) =>
|
|
209
|
+
(entry.moduleSpecifier === "node:url" || entry.moduleSpecifier === "url") &&
|
|
210
|
+
entry.namedLocals.includes("fileURLToPath")
|
|
211
|
+
)
|
|
212
|
+
if (!nodeUrlImport) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
"unsupported Vite config: fileURLToPath must be imported by name from node:url."
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (staticRoot.kind === "path-default") {
|
|
220
|
+
const nodePathImport = imports.find(
|
|
221
|
+
(entry) =>
|
|
222
|
+
(entry.moduleSpecifier === "node:path" || entry.moduleSpecifier === "path") &&
|
|
223
|
+
entry.defaultLocal === "path"
|
|
224
|
+
)
|
|
225
|
+
if (!nodePathImport) {
|
|
226
|
+
throw new Error(
|
|
227
|
+
"unsupported Vite config: path must be a default import from node:path."
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (staticRoot.kind === "path-named") {
|
|
233
|
+
const nodePathImport = imports.find(
|
|
234
|
+
(entry) =>
|
|
235
|
+
(entry.moduleSpecifier === "node:path" || entry.moduleSpecifier === "path") &&
|
|
236
|
+
entry.namedLocals.includes("resolve")
|
|
237
|
+
)
|
|
238
|
+
if (!nodePathImport) {
|
|
239
|
+
throw new Error(
|
|
240
|
+
"unsupported Vite config: resolve must be imported by name from node:path."
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function validateViteConfigStructure(source) {
|
|
247
|
+
const configObject = findViteConfigObject(source)
|
|
248
|
+
assertStaticObjectStructure(source, configObject, "config")
|
|
249
|
+
|
|
250
|
+
const resolveProperty = findObjectProperty(source, configObject, "resolve")
|
|
251
|
+
if (!resolveProperty) return
|
|
252
|
+
|
|
253
|
+
const resolveObject = expectObjectLiteralValue(
|
|
254
|
+
source,
|
|
255
|
+
resolveProperty,
|
|
256
|
+
"unsupported Vite config: resolve must be an object literal."
|
|
257
|
+
)
|
|
258
|
+
assertStaticObjectStructure(source, resolveObject, "resolve")
|
|
259
|
+
|
|
260
|
+
const aliasProperty = findObjectProperty(source, resolveObject, "alias")
|
|
261
|
+
if (!aliasProperty) return
|
|
262
|
+
|
|
263
|
+
const aliasObject = expectObjectLiteralValue(
|
|
264
|
+
source,
|
|
265
|
+
aliasProperty,
|
|
266
|
+
"unsupported Vite config: resolve.alias must be an object literal."
|
|
267
|
+
)
|
|
268
|
+
assertStaticObjectStructure(source, aliasObject, "resolve.alias")
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function assertStaticObjectStructure(source, objectRange, label) {
|
|
272
|
+
const bodyEnd = objectRange.end - 1
|
|
273
|
+
const seen = new Set()
|
|
274
|
+
let index = objectRange.start + 1
|
|
275
|
+
|
|
276
|
+
while (index < bodyEnd) {
|
|
277
|
+
index = skipWhitespaceCommentsAndCommas(source, index, bodyEnd)
|
|
278
|
+
if (index >= bodyEnd) return
|
|
279
|
+
|
|
280
|
+
const propertyStart = index
|
|
281
|
+
const propertyEnd = findTopLevelCommaOrEnd(
|
|
282
|
+
source,
|
|
283
|
+
propertyStart,
|
|
284
|
+
bodyEnd
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
if (source.startsWith("...", propertyStart)) {
|
|
288
|
+
throw new Error(
|
|
289
|
+
`unsupported Vite config: ${label} cannot contain spread properties.`
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
if (source[propertyStart] === "[") {
|
|
293
|
+
throw new Error(
|
|
294
|
+
`unsupported Vite config: ${label} cannot contain computed properties.`
|
|
295
|
+
)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const name = readPropertyName(source, propertyStart)
|
|
299
|
+
if (name) {
|
|
300
|
+
if (seen.has(name.name)) {
|
|
301
|
+
throw new Error(
|
|
302
|
+
`unsupported Vite config: ${label} contains duplicate property ${JSON.stringify(name.name)}.`
|
|
303
|
+
)
|
|
304
|
+
}
|
|
305
|
+
seen.add(name.name)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
index = propertyEnd + 1
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function getTailwindViteLocalName(source) {
|
|
313
|
+
const tailwindImport = parseImportDeclarations(source).find(
|
|
314
|
+
(entry) => entry.moduleSpecifier === "@tailwindcss/vite"
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
if (!tailwindImport) return "tailwindcss"
|
|
318
|
+
if (tailwindImport.defaultLocal) return tailwindImport.defaultLocal
|
|
319
|
+
|
|
320
|
+
throw new Error(
|
|
321
|
+
"unsupported Vite config: @tailwindcss/vite must be imported with a default binding."
|
|
322
|
+
)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function addViteResolveAlias(source, aliasRoot, sourceRoot) {
|
|
326
|
+
const configObject = findViteConfigObject(source)
|
|
327
|
+
const resolveProperty = findObjectProperty(source, configObject, "resolve")
|
|
328
|
+
const aliasEntry = `${JSON.stringify(aliasRoot)}: fileURLToPath(new URL(${JSON.stringify(
|
|
329
|
+
sourceRoot
|
|
330
|
+
)}, import.meta.url))`
|
|
331
|
+
|
|
332
|
+
if (!resolveProperty) {
|
|
333
|
+
return insertObjectProperty(
|
|
334
|
+
source,
|
|
335
|
+
configObject,
|
|
336
|
+
[
|
|
337
|
+
"resolve: {",
|
|
338
|
+
" alias: {",
|
|
339
|
+
` ${aliasEntry},`,
|
|
340
|
+
" },",
|
|
341
|
+
"},",
|
|
342
|
+
].join("\n")
|
|
343
|
+
)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const resolveObject = expectObjectLiteralValue(
|
|
347
|
+
source,
|
|
348
|
+
resolveProperty,
|
|
349
|
+
"unsupported Vite config: resolve must be an object literal."
|
|
350
|
+
)
|
|
351
|
+
const aliasProperty = findObjectProperty(source, resolveObject, "alias")
|
|
352
|
+
|
|
353
|
+
if (!aliasProperty) {
|
|
354
|
+
return insertObjectProperty(
|
|
355
|
+
source,
|
|
356
|
+
resolveObject,
|
|
357
|
+
[
|
|
358
|
+
"alias: {",
|
|
359
|
+
` ${aliasEntry},`,
|
|
360
|
+
"},",
|
|
361
|
+
].join("\n")
|
|
362
|
+
)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const aliasObject = expectObjectLiteralValue(
|
|
366
|
+
source,
|
|
367
|
+
aliasProperty,
|
|
368
|
+
"unsupported Vite config: resolve.alias must be an object literal."
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
if (findObjectProperty(source, aliasObject, aliasRoot)) {
|
|
372
|
+
return source
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return appendObjectProperty(source, aliasObject, `${aliasEntry},`)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function addVitePluginCall(source, pluginCall, pluginLocalName) {
|
|
379
|
+
const configObject = findViteConfigObject(source)
|
|
380
|
+
const pluginsProperty = findObjectProperty(source, configObject, "plugins")
|
|
381
|
+
|
|
382
|
+
if (!pluginsProperty) {
|
|
383
|
+
return insertObjectProperty(source, configObject, `plugins: [${pluginCall}],`)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const pluginsArray = expectArrayLiteralValue(
|
|
387
|
+
source,
|
|
388
|
+
pluginsProperty,
|
|
389
|
+
"unsupported Vite config: plugins must be an array literal."
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
if (arrayHasCallExpression(source, pluginsArray, pluginLocalName)) {
|
|
393
|
+
return source
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return appendArrayElement(source, pluginsArray, pluginCall)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function findViteConfigObject(source) {
|
|
400
|
+
const expressionStart = findExportDefaultExpressionStart(source)
|
|
401
|
+
|
|
402
|
+
if (expressionStart === -1) {
|
|
403
|
+
throw new Error(
|
|
404
|
+
"unsupported Vite config: expected `export default defineConfig({ ... })` or `export default { ... }`."
|
|
405
|
+
)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (source[expressionStart] === "{") {
|
|
409
|
+
return createBracketRange(source, expressionStart, "{")
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const callee = readIdentifierAt(source, expressionStart)
|
|
413
|
+
if (callee?.name !== "defineConfig") {
|
|
414
|
+
throw new Error(
|
|
415
|
+
"unsupported Vite config: default export must be `defineConfig({ ... })` or an object literal."
|
|
416
|
+
)
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
let index = skipWhitespaceAndComments(source, callee.end)
|
|
420
|
+
if (source[index] !== "(") {
|
|
421
|
+
throw new Error("unsupported Vite config: malformed defineConfig call.")
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
index = skipWhitespaceAndComments(source, index + 1)
|
|
425
|
+
|
|
426
|
+
// defineConfig({ ... })
|
|
427
|
+
if (source[index] === "{") {
|
|
428
|
+
return createBracketRange(source, index, "{")
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// defineConfig((env) => ({ ... })) — the other documented Vite form, used
|
|
432
|
+
// whenever the config depends on `mode` or `command`. The config object is
|
|
433
|
+
// the arrow's returned expression, so resolve to that and edit it in place.
|
|
434
|
+
const arrowBody = findConfigArrowBodyStart(source, index)
|
|
435
|
+
if (arrowBody !== -1) {
|
|
436
|
+
return createBracketRange(source, arrowBody, "{")
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
throw new Error(
|
|
440
|
+
"unsupported Vite config: defineConfig must receive a config object literal " +
|
|
441
|
+
"or an arrow function returning one, e.g. `defineConfig(({ mode }) => ({ ... }))`."
|
|
442
|
+
)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Given the index of defineConfig's first argument, return the index of the
|
|
446
|
+
// object literal an arrow function returns, or -1 when the argument is not an
|
|
447
|
+
// arrow whose body is a parenthesised object.
|
|
448
|
+
function findConfigArrowBodyStart(source, argumentStart) {
|
|
449
|
+
let index = argumentStart
|
|
450
|
+
|
|
451
|
+
// An optional `async` before the parameter list.
|
|
452
|
+
const asyncKeyword = readIdentifierAt(source, index)
|
|
453
|
+
if (asyncKeyword?.name === "async") {
|
|
454
|
+
index = skipWhitespaceAndComments(source, asyncKeyword.end)
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Parameter list: either `(...)` or a single bare identifier.
|
|
458
|
+
if (source[index] === "(") {
|
|
459
|
+
const parameters = createBracketRange(source, index, "(")
|
|
460
|
+
index = skipWhitespaceAndComments(source, parameters.end + 1)
|
|
461
|
+
} else {
|
|
462
|
+
const parameter = readIdentifierAt(source, index)
|
|
463
|
+
if (!parameter) return -1
|
|
464
|
+
index = skipWhitespaceAndComments(source, parameter.end)
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (source.slice(index, index + 2) !== "=>") return -1
|
|
468
|
+
index = skipWhitespaceAndComments(source, index + 2)
|
|
469
|
+
|
|
470
|
+
// `=> ({ ... })`. A block body (`=> { return { ... } }`) is deliberately not
|
|
471
|
+
// followed: locating the returned object there means tracking control flow,
|
|
472
|
+
// and editing it in place would be guesswork. Those users get the explicit
|
|
473
|
+
// message above rather than a silently wrong edit.
|
|
474
|
+
if (source[index] !== "(") return -1
|
|
475
|
+
index = skipWhitespaceAndComments(source, index + 1)
|
|
476
|
+
|
|
477
|
+
return source[index] === "{" ? index : -1
|
|
478
|
+
}
|