effect-start 0.21.0 → 0.22.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/README.md +1 -4
- package/dist/Cookies.js +392 -0
- package/dist/FileSystem.js +131 -0
- package/dist/Socket.js +37 -0
- package/package.json +35 -36
- package/src/Commander.ts +73 -130
- package/src/ContentNegotiation.ts +64 -95
- package/src/Cookies.ts +36 -57
- package/src/Development.ts +47 -62
- package/src/Effectify.ts +222 -206
- package/src/Entity.ts +59 -86
- package/src/FilePathPattern.ts +5 -5
- package/src/FileRouter.ts +37 -62
- package/src/FileRouterCodegen.ts +63 -55
- package/src/FileSystem.ts +46 -59
- package/src/Http.ts +17 -50
- package/src/PathPattern.ts +33 -41
- package/src/PlatformError.ts +29 -50
- package/src/PlatformRuntime.ts +39 -47
- package/src/Route.ts +68 -187
- package/src/RouteBody.ts +45 -161
- package/src/RouteHook.ts +22 -45
- package/src/RouteHttp.ts +88 -142
- package/src/RouteHttpTracer.ts +25 -26
- package/src/RouteMount.ts +100 -238
- package/src/RouteSchema.ts +67 -201
- package/src/RouteSse.ts +28 -82
- package/src/RouteTree.ts +31 -79
- package/src/RouteTrie.ts +13 -32
- package/src/SchemaExtra.ts +3 -5
- package/src/Socket.ts +5 -2
- package/src/Start.ts +20 -21
- package/src/StreamExtra.ts +93 -96
- package/src/TuplePathPattern.ts +54 -43
- package/src/Unique.ts +9 -15
- package/src/Values.ts +26 -30
- package/src/bun/BunBundle.ts +27 -73
- package/src/bun/BunImportTrackerPlugin.ts +67 -65
- package/src/bun/BunRoute.ts +12 -31
- package/src/bun/BunRuntime.ts +3 -10
- package/src/bun/BunServer.ts +55 -91
- package/src/bun/BunVirtualFilesPlugin.ts +1 -4
- package/src/bun/_BunEnhancedResolve.ts +17 -42
- package/src/bun/_empty.html +0 -1
- package/src/bundler/Bundle.ts +20 -36
- package/src/bundler/BundleFiles.ts +35 -55
- package/src/client/Overlay.ts +1 -2
- package/src/client/ScrollState.ts +5 -9
- package/src/client/index.ts +10 -13
- package/src/datastar/actions/fetch.ts +29 -48
- package/src/datastar/actions/peek.ts +1 -5
- package/src/datastar/actions/setAll.ts +2 -2
- package/src/datastar/actions/toggleAll.ts +2 -2
- package/src/datastar/attributes/attr.ts +17 -18
- package/src/datastar/attributes/bind.ts +41 -61
- package/src/datastar/attributes/class.ts +2 -5
- package/src/datastar/attributes/computed.ts +2 -10
- package/src/datastar/attributes/effect.ts +1 -2
- package/src/datastar/attributes/indicator.ts +2 -8
- package/src/datastar/attributes/init.ts +2 -10
- package/src/datastar/attributes/jsonSignals.ts +1 -6
- package/src/datastar/attributes/on.ts +4 -13
- package/src/datastar/attributes/onIntersect.ts +10 -22
- package/src/datastar/attributes/onInterval.ts +2 -10
- package/src/datastar/attributes/onSignalPatch.ts +18 -28
- package/src/datastar/attributes/ref.ts +1 -2
- package/src/datastar/attributes/show.ts +1 -2
- package/src/datastar/attributes/signals.ts +1 -5
- package/src/datastar/attributes/style.ts +6 -12
- package/src/datastar/attributes/text.ts +1 -2
- package/src/datastar/engine.ts +102 -158
- package/src/datastar/index.ts +2 -2
- package/src/datastar/utils.ts +16 -51
- package/src/datastar/watchers/patchElements.ts +35 -93
- package/src/datastar/watchers/patchSignals.ts +1 -2
- package/src/experimental/EncryptedCookies.ts +79 -142
- package/src/hyper/Hyper.ts +14 -33
- package/src/hyper/HyperHtml.ts +9 -10
- package/src/hyper/HyperNode.ts +2 -7
- package/src/hyper/HyperRoute.ts +2 -5
- package/src/hyper/jsx-runtime.ts +2 -10
- package/src/hyper/jsx.d.ts +171 -440
- package/src/lint/plugin.js +276 -0
- package/src/node/NodeFileSystem.ts +138 -186
- package/src/node/NodeUtils.ts +1 -3
- package/src/testing/TestLogger.ts +9 -22
- package/src/testing/utils.ts +30 -31
- package/src/x/cloudflare/CloudflareTunnel.ts +37 -54
- package/src/x/datastar/Datastar.ts +3 -10
- package/src/x/datastar/index.ts +1 -3
- package/src/x/datastar/jsx-datastar.d.ts +1 -4
- package/src/x/tailwind/TailwindPlugin.ts +119 -112
- package/src/x/tailwind/compile.ts +10 -33
- package/src/x/tailwind/plugin.ts +2 -2
|
@@ -11,9 +11,9 @@ import {
|
|
|
11
11
|
String,
|
|
12
12
|
} from "effect"
|
|
13
13
|
|
|
14
|
-
export class CloudflareTunnelSpawnError extends Data.TaggedError(
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
export class CloudflareTunnelSpawnError extends Data.TaggedError("CloudflareTunnelSpawnError")<{
|
|
15
|
+
cause: unknown
|
|
16
|
+
}> {}
|
|
17
17
|
|
|
18
18
|
export const start = (opts: {
|
|
19
19
|
command?: string
|
|
@@ -23,36 +23,28 @@ export const start = (opts: {
|
|
|
23
23
|
logLevel?: LogLevel.LogLevel
|
|
24
24
|
logPrefix?: string
|
|
25
25
|
}) =>
|
|
26
|
-
Effect.gen(function*() {
|
|
27
|
-
const logPrefix = String.isString(opts.logPrefix)
|
|
28
|
-
|
|
29
|
-
: "CloudflareTunnel: "
|
|
30
|
-
const args: string[] = [
|
|
26
|
+
Effect.gen(function* () {
|
|
27
|
+
const logPrefix = String.isString(opts.logPrefix) ? opts.logPrefix : "CloudflareTunnel: "
|
|
28
|
+
const args: Array<string> = [
|
|
31
29
|
"tunnel",
|
|
32
30
|
"run",
|
|
33
|
-
opts.tunnelUrl
|
|
34
|
-
? [
|
|
35
|
-
"--url",
|
|
36
|
-
opts.tunnelUrl,
|
|
37
|
-
]
|
|
38
|
-
: [],
|
|
31
|
+
opts.tunnelUrl ? ["--url", opts.tunnelUrl] : [],
|
|
39
32
|
opts.tunnelName,
|
|
40
|
-
]
|
|
41
|
-
.flatMap(v => v)
|
|
33
|
+
].flatMap((v) => v)
|
|
42
34
|
|
|
43
35
|
const proc = yield* Effect.try({
|
|
44
36
|
try: () =>
|
|
45
|
-
Bun.spawn(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
),
|
|
37
|
+
Bun.spawn([opts.command ?? "cloudflared", ...args], {
|
|
38
|
+
stderr: "pipe",
|
|
39
|
+
stdout: "pipe",
|
|
40
|
+
}),
|
|
49
41
|
catch: (err) => new CloudflareTunnelSpawnError({ cause: err }),
|
|
50
42
|
})
|
|
51
43
|
|
|
52
44
|
yield* Effect.addFinalizer(() =>
|
|
53
45
|
Effect.sync(() => {
|
|
54
46
|
proc.kill()
|
|
55
|
-
})
|
|
47
|
+
}),
|
|
56
48
|
)
|
|
57
49
|
|
|
58
50
|
yield* Effect.logInfo(
|
|
@@ -68,48 +60,39 @@ export const start = (opts: {
|
|
|
68
60
|
),
|
|
69
61
|
Stream.decodeText("utf-8"),
|
|
70
62
|
Stream.splitLines,
|
|
71
|
-
opts.cleanLogs ?? true
|
|
72
|
-
? Stream.map(v =>
|
|
73
|
-
v.replace(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z\s\w+\s/, "")
|
|
74
|
-
)
|
|
63
|
+
(opts.cleanLogs ?? true)
|
|
64
|
+
? Stream.map((v) => v.replace(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z\s\w+\s/, ""))
|
|
75
65
|
: identity,
|
|
76
|
-
logPrefix
|
|
77
|
-
|
|
78
|
-
: identity,
|
|
79
|
-
Stream.runForEach(v =>
|
|
80
|
-
Effect.logWithLevel(opts.logLevel ?? LogLevel.Debug, v)
|
|
81
|
-
),
|
|
66
|
+
logPrefix ? Stream.map((v) => logPrefix + v) : identity,
|
|
67
|
+
Stream.runForEach((v) => Effect.logWithLevel(opts.logLevel ?? LogLevel.Debug, v)),
|
|
82
68
|
)
|
|
83
69
|
})
|
|
84
70
|
|
|
85
71
|
export const layer = () =>
|
|
86
|
-
Layer.scopedDiscard(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
72
|
+
Layer.scopedDiscard(
|
|
73
|
+
Effect.gen(function* () {
|
|
74
|
+
const tunnelName = yield* pipe(
|
|
75
|
+
Config.string("CLOUDFLARE_TUNNEL_NAME"),
|
|
76
|
+
Config.option,
|
|
77
|
+
Effect.andThen(Option.getOrUndefined),
|
|
78
|
+
)
|
|
79
|
+
const tunnelUrl = yield* pipe(
|
|
80
|
+
Config.string("CLOUDFLARE_TUNNEL_URL"),
|
|
81
|
+
Config.option,
|
|
82
|
+
Effect.andThen(Option.getOrUndefined),
|
|
83
|
+
)
|
|
97
84
|
|
|
98
|
-
|
|
99
|
-
|
|
85
|
+
if (!tunnelName) {
|
|
86
|
+
yield* Effect.logWarning("CLOUDFLARE_TUNNEL_NAME not provided. Skipping.")
|
|
100
87
|
|
|
101
|
-
|
|
102
|
-
|
|
88
|
+
return
|
|
89
|
+
}
|
|
103
90
|
|
|
104
|
-
|
|
105
|
-
.forkScoped(
|
|
91
|
+
yield* Effect.forkScoped(
|
|
106
92
|
start({
|
|
107
93
|
tunnelName,
|
|
108
94
|
tunnelUrl,
|
|
109
|
-
}).pipe(
|
|
110
|
-
Effect.catchAll(err =>
|
|
111
|
-
Effect.logError("Cloudflare tunnel failed", err)
|
|
112
|
-
),
|
|
113
|
-
),
|
|
95
|
+
}).pipe(Effect.catchAll((err) => Effect.logError("Cloudflare tunnel failed", err))),
|
|
114
96
|
)
|
|
115
|
-
|
|
97
|
+
}),
|
|
98
|
+
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as HyperNode from "../../hyper/HyperNode.ts"
|
|
1
|
+
import type * as HyperNode from "../../hyper/HyperNode.ts"
|
|
2
2
|
|
|
3
3
|
export const HyperHooks = {
|
|
4
4
|
onNode,
|
|
@@ -49,18 +49,11 @@ function onNode(node: HyperNode.HyperNode) {
|
|
|
49
49
|
|
|
50
50
|
// Handle dynamic attributes with suffixes
|
|
51
51
|
for (const [key, value] of Object.entries(node.props)) {
|
|
52
|
-
if (
|
|
53
|
-
key.startsWith("data-signals-")
|
|
54
|
-
&& typeof value === "object"
|
|
55
|
-
&& value !== null
|
|
56
|
-
) {
|
|
52
|
+
if (key.startsWith("data-signals-") && typeof value === "object" && value !== null) {
|
|
57
53
|
node.props[key] = JSON.stringify(value)
|
|
58
54
|
}
|
|
59
55
|
|
|
60
|
-
if (
|
|
61
|
-
key.startsWith("data-on-")
|
|
62
|
-
&& typeof value === "function"
|
|
63
|
-
) {
|
|
56
|
+
if (key.startsWith("data-on-") && typeof value === "function") {
|
|
64
57
|
// @ts-ignore
|
|
65
58
|
node.props[key] = `(${value.toString()})()`
|
|
66
59
|
}
|
package/src/x/datastar/index.ts
CHANGED
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
type DatastarSignalsObject = Record<string, any>
|
|
3
3
|
type DatastarClassObject = Record<string, boolean | string>
|
|
4
4
|
type DatastarAttrObject = Record<string, string | boolean | number>
|
|
5
|
-
type DatastarStyleObject = Record<
|
|
6
|
-
string,
|
|
7
|
-
string | number | boolean | null | undefined
|
|
8
|
-
>
|
|
5
|
+
type DatastarStyleObject = Record<string, string | number | boolean | null | undefined>
|
|
9
6
|
|
|
10
7
|
/**
|
|
11
8
|
* Datastar attributes for reactive web applications
|
|
@@ -46,12 +46,12 @@ export const make = (opts?: {
|
|
|
46
46
|
|
|
47
47
|
const prepopulateCandidates = opts?.scanPath
|
|
48
48
|
? async () => {
|
|
49
|
-
|
|
49
|
+
const candidates = await scanFiles(opts.scanPath!)
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
scannedCandidates.clear()
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
candidates.forEach((candidate) => scannedCandidates.add(candidate))
|
|
54
|
+
}
|
|
55
55
|
: null
|
|
56
56
|
|
|
57
57
|
// Track import relationships when dynamically scanning
|
|
@@ -60,122 +60,129 @@ export const make = (opts?: {
|
|
|
60
60
|
// Better to pass scanPath explicitly.
|
|
61
61
|
// @see https://github.com/oven-sh/bun/issues/20877
|
|
62
62
|
if (!prepopulateCandidates) {
|
|
63
|
-
builder.onResolve(
|
|
64
|
-
filter: /.*/,
|
|
65
|
-
}, (args) => {
|
|
66
|
-
const fullPath = Bun.resolveSync(args.path, args.resolveDir)
|
|
67
|
-
const importer = args.importer
|
|
68
|
-
|
|
69
|
-
if (fullPath.includes("/node_modules/")) {
|
|
70
|
-
return undefined
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Register every visited module.
|
|
75
|
-
*/
|
|
63
|
+
builder.onResolve(
|
|
76
64
|
{
|
|
77
|
-
|
|
78
|
-
|
|
65
|
+
filter: /.*/,
|
|
66
|
+
},
|
|
67
|
+
(args) => {
|
|
68
|
+
const fullPath = Bun.resolveSync(args.path, args.resolveDir)
|
|
69
|
+
const importer = args.importer
|
|
70
|
+
|
|
71
|
+
if (fullPath.includes("/node_modules/")) {
|
|
72
|
+
return undefined
|
|
79
73
|
}
|
|
80
74
|
|
|
81
|
-
|
|
82
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Register every visited module.
|
|
77
|
+
*/
|
|
78
|
+
{
|
|
79
|
+
if (!importAncestors.has(fullPath)) {
|
|
80
|
+
importAncestors.set(fullPath, new Set())
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!importDescendants.has(fullPath)) {
|
|
84
|
+
importDescendants.set(fullPath, new Set())
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!importAncestors.has(importer)) {
|
|
88
|
+
importAncestors.set(args.importer, new Set())
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!importDescendants.has(importer)) {
|
|
92
|
+
importDescendants.set(importer, new Set())
|
|
93
|
+
}
|
|
83
94
|
}
|
|
84
95
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
96
|
+
importAncestors.get(fullPath)!.add(importer)
|
|
97
|
+
importDescendants.get(importer)!.add(fullPath)
|
|
88
98
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
importAncestors.get(fullPath)!.add(importer)
|
|
95
|
-
importDescendants.get(importer)!.add(fullPath)
|
|
96
|
-
|
|
97
|
-
return undefined
|
|
98
|
-
})
|
|
99
|
+
return undefined
|
|
100
|
+
},
|
|
101
|
+
)
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
/**
|
|
102
105
|
* Scan for class name candidates in component files.
|
|
103
106
|
*/
|
|
104
|
-
builder.onLoad(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
builder.onLoad(
|
|
108
|
+
{
|
|
109
|
+
filter: filesPattern,
|
|
110
|
+
},
|
|
111
|
+
async (args) => {
|
|
112
|
+
const contents = await Bun.file(args.path).text()
|
|
113
|
+
const classNames = extractClassNames(contents)
|
|
114
|
+
|
|
115
|
+
if (classNames.size > 0) {
|
|
116
|
+
classNameCandidates.set(args.path, classNames)
|
|
117
|
+
}
|
|
113
118
|
|
|
114
|
-
|
|
115
|
-
|
|
119
|
+
return undefined
|
|
120
|
+
},
|
|
121
|
+
)
|
|
116
122
|
|
|
117
123
|
/**
|
|
118
124
|
* Compile tailwind entrypoints.
|
|
119
125
|
*/
|
|
120
|
-
builder.onLoad(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
builder.onLoad(
|
|
127
|
+
{
|
|
128
|
+
filter: cssPattern,
|
|
129
|
+
},
|
|
130
|
+
async (args) => {
|
|
131
|
+
const source = await Bun.file(args.path).text()
|
|
132
|
+
|
|
133
|
+
if (!hasCssImport(source, "tailwindcss")) {
|
|
134
|
+
return undefined
|
|
135
|
+
}
|
|
128
136
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
137
|
+
const compiler = await Tailwind.compile(source, {
|
|
138
|
+
base: NPath.dirname(args.path),
|
|
139
|
+
onDependency: (path) => {},
|
|
140
|
+
})
|
|
133
141
|
|
|
134
|
-
|
|
142
|
+
await prepopulateCandidates?.()
|
|
135
143
|
|
|
136
|
-
|
|
137
|
-
|
|
144
|
+
// wait for other files to be loaded so we can collect class name candidates
|
|
145
|
+
await args.defer()
|
|
138
146
|
|
|
139
|
-
|
|
147
|
+
const candidates = new Set<string>(scannedCandidates)
|
|
140
148
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
149
|
+
// when we scan a path, we don't need to track candidate tree
|
|
150
|
+
if (!prepopulateCandidates) {
|
|
151
|
+
const pendingModules = [
|
|
152
|
+
// get class name candidates from all modules that import this one
|
|
153
|
+
...(importAncestors.get(args.path) ?? []),
|
|
154
|
+
]
|
|
155
|
+
const visitedModules = new Set<string>()
|
|
148
156
|
|
|
149
|
-
|
|
150
|
-
|
|
157
|
+
while (pendingModules.length > 0) {
|
|
158
|
+
const currentPath = pendingModules.shift()!
|
|
151
159
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
160
|
+
if (visitedModules.has(currentPath)) {
|
|
161
|
+
continue
|
|
162
|
+
}
|
|
155
163
|
|
|
156
|
-
|
|
164
|
+
const moduleImports = importDescendants.get(currentPath)
|
|
157
165
|
|
|
158
|
-
|
|
159
|
-
|
|
166
|
+
moduleImports?.forEach((moduleImport) => {
|
|
167
|
+
const moduleCandidates = classNameCandidates.get(moduleImport)
|
|
160
168
|
|
|
161
|
-
|
|
169
|
+
moduleCandidates?.forEach((candidate) => candidates.add(candidate))
|
|
162
170
|
|
|
163
|
-
|
|
164
|
-
|
|
171
|
+
pendingModules.push(moduleImport)
|
|
172
|
+
})
|
|
165
173
|
|
|
166
|
-
|
|
174
|
+
visitedModules.add(currentPath)
|
|
175
|
+
}
|
|
167
176
|
}
|
|
168
|
-
}
|
|
169
177
|
|
|
170
|
-
|
|
171
|
-
...candidates,
|
|
172
|
-
])
|
|
178
|
+
const contents = compiler.build([...candidates])
|
|
173
179
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
180
|
+
return {
|
|
181
|
+
contents,
|
|
182
|
+
loader: "css",
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
)
|
|
179
186
|
},
|
|
180
187
|
}
|
|
181
188
|
}
|
|
@@ -186,19 +193,19 @@ const TEMPLATE_EXPRESSION_REGEX = /\$\{[^}]*\}/g
|
|
|
186
193
|
const TAILWIND_CLASS_REGEX = /^[a-zA-Z0-9_:-]+(\[[^\]]*\])?$/
|
|
187
194
|
const CLASS_NAME_PATTERNS = [
|
|
188
195
|
// HTML class attributes with double quotes: <div class="bg-blue-500 text-white">
|
|
189
|
-
|
|
196
|
+
'<[^>]*?\\sclass\\s*=\\s*"([^"]+)"',
|
|
190
197
|
|
|
191
198
|
// HTML class attributes with single quotes: <div class='bg-blue-500 text-white'>
|
|
192
199
|
"<[^>]*?\\sclass\\s*=\\s*'([^']+)'",
|
|
193
200
|
|
|
194
201
|
// JSX className attributes with double quotes: <div className="bg-blue-500 text-white">
|
|
195
|
-
|
|
202
|
+
'<[^>]*?\\sclassName\\s*=\\s*"([^"]+)"',
|
|
196
203
|
|
|
197
204
|
// JSX className attributes with single quotes: <div className='bg-blue-500 text-white'>
|
|
198
205
|
"<[^>]*?\\sclassName\\s*=\\s*'([^']+)'",
|
|
199
206
|
|
|
200
207
|
// JSX className with braces and double quotes: <div className={"bg-blue-500 text-white"}>
|
|
201
|
-
|
|
208
|
+
'<[^>]*?\\sclassName\\s*=\\s*\\{\\s*"([^"]+)"\\s*\\}',
|
|
202
209
|
|
|
203
210
|
// JSX className with braces and single quotes: <div className={'bg-blue-500 text-white'}>
|
|
204
211
|
"<[^>]*?\\sclassName\\s*=\\s*\\{\\s*'([^']+)'\\s*\\}",
|
|
@@ -210,19 +217,19 @@ const CLASS_NAME_PATTERNS = [
|
|
|
210
217
|
"<[^>]*?\\sclass\\s*=\\s*\\{\\s*`([^`]*)`\\s*\\}",
|
|
211
218
|
|
|
212
219
|
// HTML class at start of tag with double quotes: <div class="bg-blue-500">
|
|
213
|
-
|
|
220
|
+
'<\\w+\\s+class\\s*=\\s*"([^"]+)"',
|
|
214
221
|
|
|
215
222
|
// HTML class at start of tag with single quotes: <div class='bg-blue-500'>
|
|
216
223
|
"<\\w+\\s+class\\s*=\\s*'([^']+)'",
|
|
217
224
|
|
|
218
225
|
// JSX className at start of tag with double quotes: <div className="bg-blue-500">
|
|
219
|
-
|
|
226
|
+
'<\\w+\\s+className\\s*=\\s*"([^"]+)"',
|
|
220
227
|
|
|
221
228
|
// JSX className at start of tag with single quotes: <div className='bg-blue-500'>
|
|
222
229
|
"<\\w+\\s+className\\s*=\\s*'([^']+)'",
|
|
223
230
|
|
|
224
231
|
// JSX className at start with braces and double quotes: <div className={"bg-blue-500"}>
|
|
225
|
-
|
|
232
|
+
'<\\w+\\s+className\\s*=\\s*\\{\\s*"([^"]+)"\\s*\\}',
|
|
226
233
|
|
|
227
234
|
// JSX className at start with braces and single quotes: <div className={'bg-blue-500'}>
|
|
228
235
|
"<\\w+\\s+className\\s*=\\s*\\{\\s*'([^']+)'\\s*\\}",
|
|
@@ -235,7 +242,7 @@ const CLASS_NAME_PATTERNS = [
|
|
|
235
242
|
]
|
|
236
243
|
|
|
237
244
|
const CLASS_NAME_REGEX = new RegExp(
|
|
238
|
-
CLASS_NAME_PATTERNS.map(pattern => `(?:${pattern})`).join("|"),
|
|
245
|
+
CLASS_NAME_PATTERNS.map((pattern) => `(?:${pattern})`).join("|"),
|
|
239
246
|
"g",
|
|
240
247
|
)
|
|
241
248
|
|
|
@@ -244,8 +251,7 @@ function hasCssImport(css: string, specifier?: string): boolean {
|
|
|
244
251
|
|
|
245
252
|
if (!importPath) return false
|
|
246
253
|
|
|
247
|
-
return specifier === undefined
|
|
248
|
-
|| importPath.includes(specifier)
|
|
254
|
+
return specifier === undefined || importPath.includes(specifier)
|
|
249
255
|
}
|
|
250
256
|
|
|
251
257
|
export function extractClassNames(source: string): Set<string> {
|
|
@@ -270,17 +276,20 @@ export function extractClassNames(source: string): Set<string> {
|
|
|
270
276
|
const staticParts = classString.split(TEMPLATE_EXPRESSION_REGEX)
|
|
271
277
|
|
|
272
278
|
for (const part of staticParts) {
|
|
273
|
-
const names = part
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
+
const names = part
|
|
280
|
+
.trim()
|
|
281
|
+
.split(/\s+/)
|
|
282
|
+
.filter((name) => {
|
|
283
|
+
if (name.length === 0) return false
|
|
284
|
+
if (name.endsWith("-") || name.startsWith("-")) return false
|
|
285
|
+
return TAILWIND_CLASS_REGEX.test(name)
|
|
286
|
+
})
|
|
287
|
+
names.forEach((name) => candidates.add(name))
|
|
279
288
|
}
|
|
280
289
|
} else {
|
|
281
290
|
// Simple case: regular class string without expressions
|
|
282
|
-
const names = classString.split(/\s+/).filter(name => name.length > 0)
|
|
283
|
-
names.forEach(name => candidates.add(name))
|
|
291
|
+
const names = classString.split(/\s+/).filter((name) => name.length > 0)
|
|
292
|
+
names.forEach((name) => candidates.add(name))
|
|
284
293
|
}
|
|
285
294
|
}
|
|
286
295
|
|
|
@@ -291,12 +300,10 @@ async function scanFiles(dir: string): Promise<Set<string>> {
|
|
|
291
300
|
const candidates = new Set<string>()
|
|
292
301
|
const glob = new Bun.Glob("**/*.{js,jsx,ts,tsx,html,vue,svelte,astro}")
|
|
293
302
|
|
|
294
|
-
for await (
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
})
|
|
299
|
-
) {
|
|
303
|
+
for await (const filePath of glob.scan({
|
|
304
|
+
cwd: dir,
|
|
305
|
+
absolute: true,
|
|
306
|
+
})) {
|
|
300
307
|
if (filePath.includes("/node_modules/")) {
|
|
301
308
|
continue
|
|
302
309
|
}
|
|
@@ -1,25 +1,14 @@
|
|
|
1
1
|
import fsPromises from "node:fs/promises"
|
|
2
2
|
import path from "node:path"
|
|
3
3
|
import { pathToFileURL } from "node:url"
|
|
4
|
-
import {
|
|
5
|
-
compile as _compile,
|
|
6
|
-
compileAst as _compileAst,
|
|
7
|
-
Features,
|
|
8
|
-
Polyfills,
|
|
9
|
-
} from "tailwindcss"
|
|
4
|
+
import { compile as _compile, compileAst as _compileAst, Features, Polyfills } from "tailwindcss"
|
|
10
5
|
import * as BunEnhancedResolve from "../../bun/_BunEnhancedResolve"
|
|
11
6
|
|
|
12
7
|
type AstNode = Parameters<typeof _compileAst>[0][number]
|
|
13
8
|
|
|
14
|
-
export {
|
|
15
|
-
Features,
|
|
16
|
-
Polyfills,
|
|
17
|
-
}
|
|
9
|
+
export { Features, Polyfills }
|
|
18
10
|
|
|
19
|
-
export type Resolver = (
|
|
20
|
-
id: string,
|
|
21
|
-
base: string,
|
|
22
|
-
) => Promise<string | false | undefined>
|
|
11
|
+
export type Resolver = (id: string, base: string) => Promise<string | false | undefined>
|
|
23
12
|
|
|
24
13
|
export interface CompileOptions {
|
|
25
14
|
base: string
|
|
@@ -48,12 +37,7 @@ function createCompileOptions({
|
|
|
48
37
|
return loadModule(id, base, onDependency, customJsResolver)
|
|
49
38
|
},
|
|
50
39
|
async loadStylesheet(id: string, sheetBase: string) {
|
|
51
|
-
let sheet = await loadStylesheet(
|
|
52
|
-
id,
|
|
53
|
-
sheetBase,
|
|
54
|
-
onDependency,
|
|
55
|
-
customCssResolver,
|
|
56
|
-
)
|
|
40
|
+
let sheet = await loadStylesheet(id, sheetBase, onDependency, customCssResolver)
|
|
57
41
|
|
|
58
42
|
return sheet
|
|
59
43
|
},
|
|
@@ -66,7 +50,7 @@ async function ensureSourceDetectionRootExists(compiler: {
|
|
|
66
50
|
// Verify if the `source(…)` path exists (until the glob pattern starts)
|
|
67
51
|
if (compiler.root && compiler.root !== "none") {
|
|
68
52
|
let globSymbols = /[*{]/
|
|
69
|
-
let basePath: string
|
|
53
|
+
let basePath: Array<string> = []
|
|
70
54
|
for (let segment of compiler.root.pattern.split("/")) {
|
|
71
55
|
if (globSymbols.test(segment)) {
|
|
72
56
|
break
|
|
@@ -89,7 +73,7 @@ async function ensureSourceDetectionRootExists(compiler: {
|
|
|
89
73
|
}
|
|
90
74
|
|
|
91
75
|
export async function compileAst(
|
|
92
|
-
ast: AstNode
|
|
76
|
+
ast: Array<AstNode>,
|
|
93
77
|
options: CompileOptions,
|
|
94
78
|
): ReturnType<typeof _compileAst> {
|
|
95
79
|
let compiler = await _compileAst(ast, createCompileOptions(options))
|
|
@@ -97,10 +81,7 @@ export async function compileAst(
|
|
|
97
81
|
return compiler
|
|
98
82
|
}
|
|
99
83
|
|
|
100
|
-
export async function compile(
|
|
101
|
-
css: string,
|
|
102
|
-
options: CompileOptions,
|
|
103
|
-
): ReturnType<typeof _compile> {
|
|
84
|
+
export async function compile(css: string, options: CompileOptions): ReturnType<typeof _compile> {
|
|
104
85
|
let compiler = await _compile(css, createCompileOptions(options))
|
|
105
86
|
await ensureSourceDetectionRootExists(compiler)
|
|
106
87
|
return compiler
|
|
@@ -131,9 +112,7 @@ export async function loadModule(
|
|
|
131
112
|
throw new Error(`Could not resolve '${id}' from '${base}'`)
|
|
132
113
|
}
|
|
133
114
|
|
|
134
|
-
let
|
|
135
|
-
importModule(pathToFileURL(resolvedPath).href + "?id=" + Date.now()),
|
|
136
|
-
])
|
|
115
|
+
let module = await importModule(pathToFileURL(resolvedPath).href + "?id=" + Date.now())
|
|
137
116
|
|
|
138
117
|
return {
|
|
139
118
|
path: resolvedPath,
|
|
@@ -230,9 +209,7 @@ async function resolveJsId(
|
|
|
230
209
|
}
|
|
231
210
|
}
|
|
232
211
|
|
|
233
|
-
return runResolver(esmResolver, id, base).catch(() =>
|
|
234
|
-
runResolver(cjsResolver, id, base)
|
|
235
|
-
)
|
|
212
|
+
return runResolver(esmResolver, id, base).catch(() => runResolver(cjsResolver, id, base))
|
|
236
213
|
}
|
|
237
214
|
|
|
238
215
|
function runResolver(
|
|
@@ -244,6 +221,6 @@ function runResolver(
|
|
|
244
221
|
resolver.resolve({}, base, id, {}, (err, result) => {
|
|
245
222
|
if (err) return reject(err)
|
|
246
223
|
resolve(result)
|
|
247
|
-
})
|
|
224
|
+
}),
|
|
248
225
|
)
|
|
249
226
|
}
|
package/src/x/tailwind/plugin.ts
CHANGED
|
@@ -8,8 +8,8 @@ const packageJson = await NodeUtils.findClosestPackageJson(process.cwd())
|
|
|
8
8
|
const scanPath = dirParam
|
|
9
9
|
? NPath.resolve(process.cwd(), dirParam)
|
|
10
10
|
: packageJson
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
? NPath.dirname(packageJson)
|
|
12
|
+
: process.cwd()
|
|
13
13
|
|
|
14
14
|
// Export as default to be used in bunfig.toml
|
|
15
15
|
export default TailwindPlugin.make({
|