effect-start 0.25.0 → 0.27.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.
Files changed (174) hide show
  1. package/package.json +20 -86
  2. package/src/Entity.ts +6 -6
  3. package/src/FileRouterCodegen.ts +4 -4
  4. package/src/FileSystem.ts +4 -8
  5. package/src/RouteHook.ts +1 -1
  6. package/src/RouteSse.ts +3 -3
  7. package/src/SqlIntrospect.ts +2 -2
  8. package/src/Start.ts +102 -2
  9. package/src/Values.ts +11 -0
  10. package/src/bun/BunRoute.ts +1 -1
  11. package/src/bun/BunRuntime.ts +5 -5
  12. package/src/hyper/HyperHtml.ts +11 -7
  13. package/src/hyper/jsx.d.ts +1 -1
  14. package/src/lint/plugin.js +174 -4
  15. package/src/sql/SqlClient.ts +355 -0
  16. package/src/sql/bun/index.ts +117 -50
  17. package/src/sql/index.ts +1 -1
  18. package/src/sql/libsql/index.ts +91 -77
  19. package/src/sql/libsql/libsql.d.ts +4 -1
  20. package/src/sql/mssql/index.ts +141 -108
  21. package/src/sql/mssql/mssql.d.ts +1 -0
  22. package/src/testing/TestLogger.ts +4 -4
  23. package/src/x/tailwind/compile.ts +6 -14
  24. package/dist/ChildProcess.js +0 -42
  25. package/dist/Commander.js +0 -410
  26. package/dist/ContentNegotiation.js +0 -465
  27. package/dist/Cookies.js +0 -371
  28. package/dist/Development.js +0 -94
  29. package/dist/Effectify.js +0 -27
  30. package/dist/Entity.js +0 -289
  31. package/dist/Fetch.js +0 -192
  32. package/dist/FilePathPattern.js +0 -97
  33. package/dist/FileRouter.js +0 -204
  34. package/dist/FileRouterCodegen.js +0 -298
  35. package/dist/FileSystem.js +0 -132
  36. package/dist/Http.js +0 -107
  37. package/dist/PathPattern.js +0 -451
  38. package/dist/PlatformError.js +0 -40
  39. package/dist/PlatformRuntime.js +0 -71
  40. package/dist/Route.js +0 -143
  41. package/dist/RouteBody.js +0 -92
  42. package/dist/RouteError.js +0 -76
  43. package/dist/RouteHook.js +0 -64
  44. package/dist/RouteHttp.js +0 -367
  45. package/dist/RouteHttpTracer.js +0 -90
  46. package/dist/RouteMount.js +0 -86
  47. package/dist/RouteSchema.js +0 -271
  48. package/dist/RouteSse.js +0 -94
  49. package/dist/RouteTree.js +0 -119
  50. package/dist/RouteTrie.js +0 -179
  51. package/dist/SchemaExtra.js +0 -99
  52. package/dist/Socket.js +0 -40
  53. package/dist/SqlIntrospect.js +0 -515
  54. package/dist/Start.js +0 -79
  55. package/dist/StartApp.js +0 -3
  56. package/dist/StreamExtra.js +0 -135
  57. package/dist/System.js +0 -38
  58. package/dist/TuplePathPattern.js +0 -74
  59. package/dist/Unique.js +0 -226
  60. package/dist/Values.js +0 -52
  61. package/dist/bun/BunBundle.js +0 -186
  62. package/dist/bun/BunChildProcessSpawner.js +0 -142
  63. package/dist/bun/BunImportTrackerPlugin.js +0 -91
  64. package/dist/bun/BunRoute.js +0 -157
  65. package/dist/bun/BunRuntime.js +0 -41
  66. package/dist/bun/BunServer.js +0 -285
  67. package/dist/bun/BunVirtualFilesPlugin.js +0 -54
  68. package/dist/bun/_BunEnhancedResolve.js +0 -127
  69. package/dist/bun/index.js +0 -5
  70. package/dist/bundler/Bundle.js +0 -92
  71. package/dist/bundler/BundleFiles.js +0 -154
  72. package/dist/bundler/BundleRoute.js +0 -62
  73. package/dist/client/Overlay.js +0 -33
  74. package/dist/client/ScrollState.js +0 -106
  75. package/dist/client/index.js +0 -97
  76. package/dist/console/Console.js +0 -42
  77. package/dist/console/ConsoleErrors.js +0 -211
  78. package/dist/console/ConsoleLogger.js +0 -56
  79. package/dist/console/ConsoleMetrics.js +0 -72
  80. package/dist/console/ConsoleProcess.js +0 -59
  81. package/dist/console/ConsoleStore.js +0 -72
  82. package/dist/console/ConsoleTracer.js +0 -107
  83. package/dist/console/Simulation.js +0 -784
  84. package/dist/console/index.js +0 -3
  85. package/dist/console/routes/tree.js +0 -30
  86. package/dist/datastar/actions/fetch.js +0 -536
  87. package/dist/datastar/actions/peek.js +0 -13
  88. package/dist/datastar/actions/setAll.js +0 -19
  89. package/dist/datastar/actions/toggleAll.js +0 -19
  90. package/dist/datastar/attributes/attr.js +0 -49
  91. package/dist/datastar/attributes/bind.js +0 -194
  92. package/dist/datastar/attributes/class.js +0 -54
  93. package/dist/datastar/attributes/computed.js +0 -25
  94. package/dist/datastar/attributes/effect.js +0 -10
  95. package/dist/datastar/attributes/indicator.js +0 -33
  96. package/dist/datastar/attributes/init.js +0 -27
  97. package/dist/datastar/attributes/jsonSignals.js +0 -33
  98. package/dist/datastar/attributes/on.js +0 -81
  99. package/dist/datastar/attributes/onIntersect.js +0 -53
  100. package/dist/datastar/attributes/onInterval.js +0 -31
  101. package/dist/datastar/attributes/onSignalPatch.js +0 -51
  102. package/dist/datastar/attributes/ref.js +0 -11
  103. package/dist/datastar/attributes/show.js +0 -32
  104. package/dist/datastar/attributes/signals.js +0 -18
  105. package/dist/datastar/attributes/style.js +0 -57
  106. package/dist/datastar/attributes/text.js +0 -29
  107. package/dist/datastar/engine.js +0 -1145
  108. package/dist/datastar/index.js +0 -25
  109. package/dist/datastar/utils.js +0 -250
  110. package/dist/datastar/watchers/patchElements.js +0 -486
  111. package/dist/datastar/watchers/patchSignals.js +0 -14
  112. package/dist/experimental/EncryptedCookies.js +0 -328
  113. package/dist/experimental/index.js +0 -1
  114. package/dist/hyper/Hyper.js +0 -28
  115. package/dist/hyper/HyperHtml.js +0 -165
  116. package/dist/hyper/HyperNode.js +0 -13
  117. package/dist/hyper/HyperRoute.js +0 -45
  118. package/dist/hyper/html.js +0 -30
  119. package/dist/hyper/index.js +0 -5
  120. package/dist/hyper/jsx-runtime.js +0 -14
  121. package/dist/index.js +0 -8
  122. package/dist/node/NodeFileSystem.js +0 -675
  123. package/dist/node/NodeUtils.js +0 -23
  124. package/dist/sql/Sql.js +0 -8
  125. package/dist/sql/bun/index.js +0 -142
  126. package/dist/sql/index.js +0 -1
  127. package/dist/sql/libsql/index.js +0 -156
  128. package/dist/sql/mssql/docker.js +0 -110
  129. package/dist/sql/mssql/index.js +0 -194
  130. package/dist/testing/TestLogger.js +0 -42
  131. package/dist/testing/index.js +0 -2
  132. package/dist/testing/utils.js +0 -61
  133. package/dist/x/cloudflare/CloudflareTunnel.js +0 -63
  134. package/dist/x/cloudflare/index.js +0 -1
  135. package/dist/x/tailscale/TailscaleTunnel.js +0 -94
  136. package/dist/x/tailscale/index.js +0 -1
  137. package/dist/x/tailwind/TailwindPlugin.js +0 -294
  138. package/dist/x/tailwind/compile.js +0 -210
  139. package/dist/x/tailwind/plugin.js +0 -17
  140. package/src/console/Console.ts +0 -42
  141. package/src/console/ConsoleErrors.ts +0 -213
  142. package/src/console/ConsoleLogger.ts +0 -56
  143. package/src/console/ConsoleMetrics.ts +0 -72
  144. package/src/console/ConsoleProcess.ts +0 -59
  145. package/src/console/ConsoleStore.ts +0 -187
  146. package/src/console/ConsoleTracer.ts +0 -107
  147. package/src/console/Simulation.ts +0 -814
  148. package/src/console/console.html +0 -340
  149. package/src/console/index.ts +0 -3
  150. package/src/console/routes/errors/route.tsx +0 -97
  151. package/src/console/routes/fiberDetail.tsx +0 -54
  152. package/src/console/routes/fibers/route.tsx +0 -45
  153. package/src/console/routes/git/route.tsx +0 -64
  154. package/src/console/routes/layout.tsx +0 -4
  155. package/src/console/routes/logs/route.tsx +0 -77
  156. package/src/console/routes/metrics/route.tsx +0 -36
  157. package/src/console/routes/route.tsx +0 -8
  158. package/src/console/routes/routes/route.tsx +0 -30
  159. package/src/console/routes/services/route.tsx +0 -21
  160. package/src/console/routes/system/route.tsx +0 -43
  161. package/src/console/routes/traceDetail.tsx +0 -22
  162. package/src/console/routes/traces/route.tsx +0 -81
  163. package/src/console/routes/tree.ts +0 -30
  164. package/src/console/ui/Errors.tsx +0 -76
  165. package/src/console/ui/Fibers.tsx +0 -321
  166. package/src/console/ui/Git.tsx +0 -182
  167. package/src/console/ui/Logs.tsx +0 -46
  168. package/src/console/ui/Metrics.tsx +0 -78
  169. package/src/console/ui/Routes.tsx +0 -125
  170. package/src/console/ui/Services.tsx +0 -273
  171. package/src/console/ui/Shell.tsx +0 -62
  172. package/src/console/ui/System.tsx +0 -131
  173. package/src/console/ui/Traces.tsx +0 -426
  174. package/src/sql/Sql.ts +0 -51
@@ -1,61 +0,0 @@
1
- import * as Array from "effect/Array"
2
- import * as Effect from "effect/Effect"
3
- import * as Function from "effect/Function"
4
- import * as Layer from "effect/Layer"
5
- import * as Logger from "effect/Logger"
6
-
7
- /**
8
- * Creates a scoped Effects and runs is asynchronously.
9
- * Useful for testing.
10
- */
11
- export const effectFn =
12
- (layer) =>
13
- (
14
- f,
15
- ) =>
16
- Function.pipe(
17
- Effect.gen(f),
18
- Effect.scoped,
19
- Effect.provide(Logger.pretty),
20
- Effect.provide(layer ?? Layer.empty),
21
- // @ts-expect-error will have to figure out how to clear deps
22
- Effect.runPromise,
23
- (v) => v.then(() => {}, clearStackTraces),
24
- )
25
-
26
- /*
27
- * When effect fails, instead of throwing FiberFailure,
28
- * throw a plain Error with the strack trace and hides
29
- * effect internals.
30
- * Otherwise, at least on Bun, the strack trace is repeated,
31
- * with some junks in between taking half of the screen.
32
- *
33
- * Direct children that starts with a dot are excluded because
34
- * some tools, like effect-start, use it to generate temporary
35
- * files that are then loaded into a runtime.
36
- */
37
- const clearStackTraces = (err) => {
38
- const ExternalStackTraceLineRegexp = /\(.*\/node_modules\/[^.]/
39
-
40
- const message =
41
- err instanceof Error
42
- ? err.message
43
- : typeof err === "object" && err !== null && "message" in err
44
- ? String(err.message)
45
- : String(err)
46
- const stack =
47
- err instanceof Error
48
- ? (err.stack ?? "")
49
- : typeof err === "object" && err !== null && "stack" in err
50
- ? String(err.stack)
51
- : ""
52
-
53
- const newErr = new Error(message)
54
- newErr.stack = Function.pipe(
55
- stack.split("\n"),
56
- Array.takeWhile((s) => !ExternalStackTraceLineRegexp.test(s)),
57
- Array.join("\n"),
58
- )
59
-
60
- throw newErr
61
- }
@@ -1,63 +0,0 @@
1
- import * as System from "../../System.js"
2
- import { Config, Effect, Layer, LogLevel, Option, pipe, Stream, String } from "effect"
3
-
4
- export const start = (opts) =>
5
- Effect.gen(function* () {
6
- const command = opts.command ?? "cloudflared"
7
- yield* System.which(command)
8
- const logPrefix = String.isString(opts.logPrefix) ? opts.logPrefix : "CloudflareTunnel: "
9
- const args = [
10
- "tunnel",
11
- "run",
12
- opts.tunnelUrl ? ["--url", opts.tunnelUrl] : [],
13
- opts.tunnelName,
14
- ].flatMap((v) => v)
15
-
16
- const proc = yield* System.spawn(command, args)
17
-
18
- yield* Effect.logInfo(
19
- `Cloudflare tunnel started name=${opts.tunnelName} pid=${proc.pid} tunnelUrl=${
20
- opts.tunnelUrl ?? "<empty>"
21
- }`,
22
- )
23
-
24
- yield* pipe(
25
- Stream.merge(proc.stdout, proc.stderr),
26
- Stream.decodeText("utf-8"),
27
- Stream.splitLines,
28
- (opts.cleanLogs ?? true)
29
- ? Stream.map((v) => v.replace(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z\s\w+\s/, ""))
30
- : (s) => s,
31
- logPrefix ? Stream.map((v) => logPrefix + v) : (s) => s,
32
- Stream.runForEach((v) => Effect.logWithLevel(opts.logLevel ?? LogLevel.Debug, v)),
33
- )
34
- })
35
-
36
- export const layer = () =>
37
- Layer.scopedDiscard(
38
- Effect.gen(function* () {
39
- const tunnelName = yield* pipe(
40
- Config.string("CLOUDFLARE_TUNNEL_NAME"),
41
- Config.option,
42
- Effect.andThen(Option.getOrUndefined),
43
- )
44
- const tunnelUrl = yield* pipe(
45
- Config.string("CLOUDFLARE_TUNNEL_URL"),
46
- Config.option,
47
- Effect.andThen(Option.getOrUndefined),
48
- )
49
-
50
- if (!tunnelName) {
51
- yield* Effect.logWarning("CLOUDFLARE_TUNNEL_NAME not provided. Skipping.")
52
-
53
- return
54
- }
55
-
56
- yield* Effect.forkScoped(
57
- start({
58
- tunnelName,
59
- tunnelUrl,
60
- }).pipe(Effect.catchAll((err) => Effect.logError("Cloudflare tunnel failed", err))),
61
- )
62
- }),
63
- )
@@ -1 +0,0 @@
1
- export * as CloudflareTunnel from "./CloudflareTunnel.js"
@@ -1,94 +0,0 @@
1
- import * as PlatformError from "../../PlatformError.js"
2
- import * as StartApp from "../../StartApp.js"
3
- import * as System from "../../System.js"
4
- import * as Deferred from "effect/Deferred"
5
- import * as Effect from "effect/Effect"
6
- import * as Layer from "effect/Layer"
7
- import * as LogLevel from "effect/LogLevel"
8
- import * as Stream from "effect/Stream"
9
- import * as Function from "effect/Function"
10
-
11
- const getStatus = (command) =>
12
- Effect.gen(function* () {
13
- const proc = yield* System.spawn(command, ["status", "--json"])
14
- const exitCode = yield* proc.exitCode
15
-
16
- if (exitCode !== 0) {
17
- const stderr = yield* proc.stderr.pipe(Stream.decodeText("utf-8"), Stream.mkString)
18
- return yield* new PlatformError.SystemError({
19
- reason: "Unknown",
20
- module: "TailscaleTunnel",
21
- method: "getStatus",
22
- description: `tailscale status exited with code ${exitCode}: ${stderr}`,
23
- })
24
- }
25
-
26
- const stdout = yield* proc.stdout.pipe(Stream.decodeText("utf-8"), Stream.mkString)
27
- const json = JSON.parse(stdout)
28
-
29
- if (json.BackendState !== "Running") {
30
- return yield* new PlatformError.SystemError({
31
- reason: "Unknown",
32
- module: "TailscaleTunnel",
33
- method: "getStatus",
34
- description: `tailscale is in state "${json.BackendState}", expected "Running"`,
35
- })
36
- }
37
-
38
- return json
39
- })
40
-
41
- const serve = (opts) =>
42
- Effect.gen(function* () {
43
- const logPrefix = opts.logPrefix ?? "TailscaleTunnel: "
44
- const args = [opts.public ? "funnel" : "serve", String(opts.port)]
45
-
46
- const proc = yield* System.spawn(opts.command, args)
47
-
48
- yield* Function.pipe(
49
- Stream.merge(proc.stdout, proc.stderr),
50
- Stream.decodeText("utf-8"),
51
- Stream.splitLines,
52
- logPrefix ? Stream.map((v) => logPrefix + v) : (s) => s,
53
- Stream.runForEach((v) => Effect.logWithLevel(opts.logLevel ?? LogLevel.Debug, v)),
54
- )
55
- })
56
-
57
- export const start = (opts) =>
58
- Effect.gen(function* () {
59
- const command = opts.command ?? "tailscale"
60
- yield* System.which(command)
61
- const status = yield* getStatus(command)
62
- const dnsName = status.Self?.DNSName?.replace(/\.$/, "")
63
- yield* serve({ ...opts, command, dnsName })
64
- })
65
-
66
- export const layer = (opts) =>
67
- Layer.scopedDiscard(
68
- Effect.gen(function* () {
69
- yield* Effect.forkScoped(
70
- Effect.gen(function* () {
71
- const app = yield* StartApp.StartApp
72
- const { server } = yield* Deferred.await(app.server)
73
- const port = server.port
74
- const command = "tailscale"
75
-
76
- yield* System.which(command)
77
- const status = yield* getStatus(command)
78
- const dnsName = status.Self?.DNSName?.replace(/\.$/, "")
79
-
80
- const serveUrl = dnsName ? `https://${dnsName}` : undefined
81
- yield* Effect.logInfo(
82
- `Tailscale ${opts?.public ? "funnel" : "serve"}${serveUrl ? ` url=${serveUrl}` : ""}`,
83
- )
84
-
85
- yield* serve({
86
- command,
87
- port,
88
- dnsName,
89
- public: opts?.public,
90
- })
91
- }).pipe(Effect.tapError((e) => Effect.logError(`Tailscale: ${e.description}`))),
92
- )
93
- }),
94
- )
@@ -1 +0,0 @@
1
- export * as TailscaleTunnel from "./TailscaleTunnel.js"
@@ -1,294 +0,0 @@
1
- import * as NPath from "node:path"
2
- import * as Tailwind from "./compile.js"
3
-
4
- export const make = (opts) => {
5
- const {
6
- filesPattern = /\.(jsx?|tsx?|html|svelte|vue|astro)$/,
7
- cssPattern = /\.css$/,
8
- target = "browser",
9
- } = opts ?? {}
10
-
11
- return {
12
- name: "Tailwind.css plugin",
13
- target,
14
- async setup(builder) {
15
- const scannedCandidates = new Set()
16
- // (file) -> (class names)
17
- const classNameCandidates = new Map()
18
- // (importer path) -> (imported paths)
19
- const importAncestors = new Map()
20
- // (imported path) -> (importer paths)
21
- const importDescendants = new Map()
22
-
23
- const prepopulateCandidates = opts?.scanPath
24
- ? async () => {
25
- const candidates = await scanFiles(opts.scanPath)
26
-
27
- scannedCandidates.clear()
28
-
29
- candidates.forEach((candidate) => scannedCandidates.add(candidate))
30
- }
31
- : null
32
-
33
- // Track import relationships when dynamically scanning
34
- // from tailwind entrypoints.
35
- // As of Bun 1.3 this pathway break for Bun Full-Stack server.
36
- // Better to pass scanPath explicitly.
37
- // @see https://github.com/oven-sh/bun/issues/20877
38
- if (!prepopulateCandidates) {
39
- builder.onResolve(
40
- {
41
- filter: /.*/,
42
- },
43
- (args) => {
44
- const fullPath = Bun.resolveSync(args.path, args.resolveDir)
45
- const importer = args.importer
46
-
47
- if (fullPath.includes("/node_modules/")) {
48
- return undefined
49
- }
50
-
51
- /**
52
- * Register every visited module.
53
- */
54
- {
55
- if (!importAncestors.has(fullPath)) {
56
- importAncestors.set(fullPath, new Set())
57
- }
58
-
59
- if (!importDescendants.has(fullPath)) {
60
- importDescendants.set(fullPath, new Set())
61
- }
62
-
63
- if (!importAncestors.has(importer)) {
64
- importAncestors.set(args.importer, new Set())
65
- }
66
-
67
- if (!importDescendants.has(importer)) {
68
- importDescendants.set(importer, new Set())
69
- }
70
- }
71
-
72
- importAncestors.get(fullPath).add(importer)
73
- importDescendants.get(importer).add(fullPath)
74
-
75
- return undefined
76
- },
77
- )
78
- }
79
-
80
- /**
81
- * Scan for class name candidates in component files.
82
- */
83
- builder.onLoad(
84
- {
85
- filter: filesPattern,
86
- },
87
- async (args) => {
88
- const contents = await Bun.file(args.path).text()
89
- const classNames = extractClassNames(contents)
90
-
91
- if (classNames.size > 0) {
92
- classNameCandidates.set(args.path, classNames)
93
- }
94
-
95
- return undefined
96
- },
97
- )
98
-
99
- /**
100
- * Compile tailwind entrypoints.
101
- */
102
- builder.onLoad(
103
- {
104
- filter: cssPattern,
105
- },
106
- async (args) => {
107
- const source = await Bun.file(args.path).text()
108
-
109
- if (!hasCssImport(source, "tailwindcss")) {
110
- return undefined
111
- }
112
-
113
- const compiler = await Tailwind.compile(source, {
114
- base: NPath.dirname(args.path),
115
- onDependency: (path) => {},
116
- })
117
-
118
- await prepopulateCandidates?.()
119
-
120
- // wait for other files to be loaded so we can collect class name candidates
121
- await args.defer()
122
-
123
- const candidates = new Set(scannedCandidates)
124
-
125
- // when we scan a path, we don't need to track candidate tree
126
- if (!prepopulateCandidates) {
127
- const pendingModules = [
128
- // get class name candidates from all modules that import this one
129
- ...(importAncestors.get(args.path) ?? []),
130
- ]
131
- const visitedModules = new Set()
132
-
133
- while (pendingModules.length > 0) {
134
- const currentPath = pendingModules.shift()
135
-
136
- if (visitedModules.has(currentPath)) {
137
- continue
138
- }
139
-
140
- const moduleImports = importDescendants.get(currentPath)
141
-
142
- moduleImports?.forEach((moduleImport) => {
143
- const moduleCandidates = classNameCandidates.get(moduleImport)
144
-
145
- moduleCandidates?.forEach((candidate) => candidates.add(candidate))
146
-
147
- pendingModules.push(moduleImport)
148
- })
149
-
150
- visitedModules.add(currentPath)
151
- }
152
- }
153
-
154
- const contents = compiler.build([...candidates])
155
-
156
- return {
157
- contents,
158
- loader: "css",
159
- }
160
- },
161
- )
162
- },
163
- }
164
- }
165
-
166
- const CSS_IMPORT_REGEX = /@import\s+(?:url\()?["']?([^"')]+)["']?\)?\s*[^;]*;/
167
- const HTML_COMMENT_REGEX = /<!--[\s\S]*?-->/g
168
- const TEMPLATE_EXPRESSION_REGEX = /\$\{[^}]*\}/g
169
- const TAILWIND_CLASS_REGEX = /^[a-zA-Z0-9_:-]+(\[[^\]]*\])?$/
170
- const CLASS_NAME_PATTERNS = [
171
- // HTML class attributes with double quotes: <div class="bg-blue-500 text-white">
172
- '<[^>]*?\\sclass\\s*=\\s*"([^"]+)"',
173
-
174
- // HTML class attributes with single quotes: <div class='bg-blue-500 text-white'>
175
- "<[^>]*?\\sclass\\s*=\\s*'([^']+)'",
176
-
177
- // JSX className attributes with double quotes: <div className="bg-blue-500 text-white">
178
- '<[^>]*?\\sclassName\\s*=\\s*"([^"]+)"',
179
-
180
- // JSX className attributes with single quotes: <div className='bg-blue-500 text-white'>
181
- "<[^>]*?\\sclassName\\s*=\\s*'([^']+)'",
182
-
183
- // JSX className with braces and double quotes: <div className={"bg-blue-500 text-white"}>
184
- '<[^>]*?\\sclassName\\s*=\\s*\\{\\s*"([^"]+)"\\s*\\}',
185
-
186
- // JSX className with braces and single quotes: <div className={'bg-blue-500 text-white'}>
187
- "<[^>]*?\\sclassName\\s*=\\s*\\{\\s*'([^']+)'\\s*\\}",
188
-
189
- // JSX className with template literals: <div className={`bg-blue-500 ${variable}`}>
190
- "<[^>]*?\\sclassName\\s*=\\s*\\{\\s*`([^`]*)`\\s*\\}",
191
-
192
- // HTML class with template literals: <div class={`bg-blue-500 ${variable}`}>
193
- "<[^>]*?\\sclass\\s*=\\s*\\{\\s*`([^`]*)`\\s*\\}",
194
-
195
- // HTML class at start of tag with double quotes: <div class="bg-blue-500">
196
- '<\\w+\\s+class\\s*=\\s*"([^"]+)"',
197
-
198
- // HTML class at start of tag with single quotes: <div class='bg-blue-500'>
199
- "<\\w+\\s+class\\s*=\\s*'([^']+)'",
200
-
201
- // JSX className at start of tag with double quotes: <div className="bg-blue-500">
202
- '<\\w+\\s+className\\s*=\\s*"([^"]+)"',
203
-
204
- // JSX className at start of tag with single quotes: <div className='bg-blue-500'>
205
- "<\\w+\\s+className\\s*=\\s*'([^']+)'",
206
-
207
- // JSX className at start with braces and double quotes: <div className={"bg-blue-500"}>
208
- '<\\w+\\s+className\\s*=\\s*\\{\\s*"([^"]+)"\\s*\\}',
209
-
210
- // JSX className at start with braces and single quotes: <div className={'bg-blue-500'}>
211
- "<\\w+\\s+className\\s*=\\s*\\{\\s*'([^']+)'\\s*\\}",
212
-
213
- // JSX className at start with template literals: <div className={`bg-blue-500 ${variable}`}>
214
- "<\\w+\\s+className\\s*=\\s*\\{\\s*`([^`]*)`\\s*\\}",
215
-
216
- // HTML class at start with template literals: <div class={`bg-blue-500 ${variable}`}>
217
- "<\\w+\\s+class\\s*=\\s*\\{\\s*`([^`]*)`\\s*\\}",
218
- ]
219
-
220
- const CLASS_NAME_REGEX = new RegExp(
221
- CLASS_NAME_PATTERNS.map((pattern) => `(?:${pattern})`).join("|"),
222
- "g",
223
- )
224
-
225
- function hasCssImport(css, specifier) {
226
- const [, importPath] = css.match(CSS_IMPORT_REGEX) ?? []
227
-
228
- if (!importPath) return false
229
-
230
- return specifier === undefined || importPath.includes(specifier)
231
- }
232
-
233
- export function extractClassNames(source) {
234
- const candidates = new Set()
235
- const sourceWithoutComments = source.replace(HTML_COMMENT_REGEX, "")
236
-
237
- for (const match of sourceWithoutComments.matchAll(CLASS_NAME_REGEX)) {
238
- // Find the first non-undefined capture group (skip match[0] which is full match)
239
- let classString = ""
240
- for (let i = 1; i < match.length; i++) {
241
- if (match[i] !== undefined) {
242
- classString = match[i]
243
- break
244
- }
245
- }
246
-
247
- if (!classString) {
248
- continue
249
- }
250
-
251
- if (classString.includes("${")) {
252
- const staticParts = classString.split(TEMPLATE_EXPRESSION_REGEX)
253
-
254
- for (const part of staticParts) {
255
- const names = part
256
- .trim()
257
- .split(/\s+/)
258
- .filter((name) => {
259
- if (name.length === 0) return false
260
- if (name.endsWith("-") || name.startsWith("-")) return false
261
- return TAILWIND_CLASS_REGEX.test(name)
262
- })
263
- names.forEach((name) => candidates.add(name))
264
- }
265
- } else {
266
- // Simple case: regular class string without expressions
267
- const names = classString.split(/\s+/).filter((name) => name.length > 0)
268
- names.forEach((name) => candidates.add(name))
269
- }
270
- }
271
-
272
- return candidates
273
- }
274
-
275
- async function scanFiles(dir) {
276
- const candidates = new Set()
277
- const glob = new Bun.Glob("**/*.{js,jsx,ts,tsx,html,vue,svelte,astro}")
278
-
279
- for await (const filePath of glob.scan({
280
- cwd: dir,
281
- absolute: true,
282
- })) {
283
- if (filePath.includes("/node_modules/")) {
284
- continue
285
- }
286
-
287
- const contents = await Bun.file(filePath).text()
288
- const classNames = extractClassNames(contents)
289
-
290
- classNames.forEach((className) => candidates.add(className))
291
- }
292
-
293
- return candidates
294
- }