next-yak 6.0.0 → 6.1.0-next.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.
@@ -1,215 +1,677 @@
1
1
  // loaders/css-loader.ts
2
2
  import { relative } from "path";
3
3
 
4
- // loaders/lib/resolveCrossFileSelectors.ts
5
- import { parse } from "@babel/parser";
6
- import traverse from "@babel/traverse";
7
- import path from "path";
4
+ // cross-file-resolver/parseModule.ts
5
+ async function parseModule(context, modulePath) {
6
+ try {
7
+ const isYak = modulePath.endsWith(".yak.ts") || modulePath.endsWith(".yak.tsx") || modulePath.endsWith(".yak.js") || modulePath.endsWith(".yak.jsx");
8
+ if (isYak && context.evaluateYakModule) {
9
+ const yakModule = await context.evaluateYakModule(modulePath);
10
+ const yakExports = objectToModuleExport(yakModule);
11
+ return {
12
+ type: "yak",
13
+ exports: { importYak: false, named: yakExports, all: [] },
14
+ path: modulePath
15
+ };
16
+ }
17
+ if (context.cache?.parse === void 0) {
18
+ return uncachedParseModule(context, modulePath);
19
+ }
20
+ const cached = context.cache.parse.get(modulePath);
21
+ if (cached === void 0) {
22
+ const parsedModule = await uncachedParseModule(context, modulePath);
23
+ context.cache.parse.set(modulePath, parsedModule);
24
+ if (context.cache.parse.addDependency) {
25
+ context.cache.parse.addDependency(modulePath, modulePath);
26
+ }
27
+ return parsedModule;
28
+ }
29
+ return cached;
30
+ } catch (error) {
31
+ throw new Error(
32
+ `Error parsing file ${modulePath}: ${error.message}`
33
+ );
34
+ }
35
+ }
36
+ async function uncachedParseModule(context, modulePath) {
37
+ const exports = await context.extractExports(modulePath);
38
+ if (!exports.importYak) {
39
+ return {
40
+ type: "regular",
41
+ path: modulePath,
42
+ exports
43
+ };
44
+ }
45
+ const transformed = await context.getTransformed(modulePath);
46
+ const mixins = parseMixins(transformed.code);
47
+ const styledComponents = parseStyledComponents(
48
+ transformed.code,
49
+ context.transpilationMode
50
+ );
51
+ return {
52
+ type: "regular",
53
+ path: modulePath,
54
+ js: transformed,
55
+ exports,
56
+ styledComponents,
57
+ mixins
58
+ };
59
+ }
60
+ function parseMixins(sourceContents) {
61
+ const mixinParts = sourceContents.split("/*YAK EXPORTED MIXIN:");
62
+ let mixins = {};
63
+ for (let i = 1; i < mixinParts.length; i++) {
64
+ const [comment] = mixinParts[i].split("*/", 1);
65
+ const position = comment.indexOf("\n");
66
+ const name = comment.slice(0, position);
67
+ const value = comment.slice(position + 1);
68
+ mixins[name] = {
69
+ type: "mixin",
70
+ value,
71
+ nameParts: name.split(":").map((part) => decodeURIComponent(part))
72
+ };
73
+ }
74
+ return mixins;
75
+ }
76
+ function parseStyledComponents(sourceContents, transpilationMode) {
77
+ const styledParts = sourceContents.split("/*YAK EXPORTED STYLED:");
78
+ let styledComponents = {};
79
+ for (let i = 1; i < styledParts.length; i++) {
80
+ const [comment] = styledParts[i].split("*/", 1);
81
+ const [componentName, className] = comment.split(":");
82
+ styledComponents[componentName] = {
83
+ type: "styled-component",
84
+ nameParts: componentName.split("."),
85
+ value: transpilationMode === "Css" ? `.${className}` : `:global(.${className})`
86
+ };
87
+ }
88
+ return styledComponents;
89
+ }
90
+ function objectToModuleExport(object) {
91
+ return Object.fromEntries(
92
+ Object.entries(object).map(([key, value]) => {
93
+ if (typeof value === "string" || typeof value === "number") {
94
+ return [key, { type: "constant", value }];
95
+ } else if (value && (typeof value === "object" || Array.isArray(value))) {
96
+ return [
97
+ key,
98
+ { type: "record", value: objectToModuleExport(value) }
99
+ ];
100
+ } else {
101
+ return [key, { type: "unsupported", hint: String(value) }];
102
+ }
103
+ })
104
+ );
105
+ }
106
+
107
+ // cross-file-resolver/Errors.ts
108
+ var CauseError = class _CauseError extends Error {
109
+ constructor(message, options) {
110
+ super(
111
+ `${message}${options?.cause ? `
112
+ Caused by: ${typeof options.cause === "object" && options.cause !== null && "message" in options.cause ? options.cause.message : String(options.cause)}` : ""}`
113
+ );
114
+ if (options?.cause instanceof _CauseError && options.cause.circular) {
115
+ this.circular = true;
116
+ }
117
+ }
118
+ };
119
+ var ResolveError = class extends CauseError {
120
+ };
121
+ var CircularDependencyError = class extends CauseError {
122
+ constructor(message, options) {
123
+ super(message, options);
124
+ this.circular = true;
125
+ }
126
+ };
127
+
128
+ // cross-file-resolver/resolveCrossFileConstant.ts
8
129
  var yakCssImportRegex = (
9
130
  // Make mixin and selector non optional once we dropped support for the babel plugin
10
131
  /--yak-css-import\:\s*url\("([^"]+)",?(|mixin|selector)\)(;?)/g
11
132
  );
12
- var compilationCache = /* @__PURE__ */ new WeakMap();
13
- var getCompilationCache = (loader) => {
14
- const compilation = loader._compilation;
15
- if (!compilation) {
16
- throw new Error("Webpack compilation object not available");
133
+ async function resolveCrossFileConstant(context, filePath, css) {
134
+ if (context.cache?.resolveCrossFileConstant === void 0) {
135
+ return uncachedResolveCrossFileConstant(context, filePath, css);
17
136
  }
18
- let cache = compilationCache.get(compilation);
19
- if (!cache) {
20
- cache = {
21
- parsedFiles: /* @__PURE__ */ new Map()
22
- };
23
- compilationCache.set(compilation, cache);
137
+ const cacheKey = await sha1(filePath + ":" + css);
138
+ const cached = context.cache.resolveCrossFileConstant.get(cacheKey);
139
+ if (cached === void 0) {
140
+ const resolvedCrossFilConstantPromise = uncachedResolveCrossFileConstant(
141
+ context,
142
+ filePath,
143
+ css
144
+ );
145
+ context.cache.resolveCrossFileConstant.set(
146
+ filePath,
147
+ resolvedCrossFilConstantPromise
148
+ );
149
+ if (context.cache.resolveCrossFileConstant.addDependency) {
150
+ context.cache.resolveCrossFileConstant.addDependency(filePath, filePath);
151
+ resolvedCrossFilConstantPromise.then((value) => {
152
+ for (const dep of value.dependencies) {
153
+ context.cache.resolveCrossFileConstant.addDependency(
154
+ filePath,
155
+ dep
156
+ );
157
+ }
158
+ });
159
+ }
160
+ return resolvedCrossFilConstantPromise;
161
+ }
162
+ return cached;
163
+ }
164
+ async function uncachedResolveCrossFileConstant(context, filePath, css) {
165
+ const yakImports = await parseYakCssImport(context, filePath, css);
166
+ if (yakImports.length === 0) {
167
+ return { resolved: css, dependencies: [] };
24
168
  }
25
- return cache;
26
- };
27
- async function resolveCrossFileConstant(loader, pathContext, css) {
28
- const matches = [...css.matchAll(yakCssImportRegex)].map((match) => {
29
- const [fullMatch, encodedArguments, importKind, semicolon] = match;
30
- const [moduleSpecifier, ...specifier] = encodedArguments.split(":").map((entry) => decodeURIComponent(entry));
31
- return {
32
- encodedArguments,
33
- moduleSpecifier,
34
- specifier,
35
- importKind,
36
- semicolon,
37
- position: match.index,
38
- size: fullMatch.length
39
- };
40
- });
41
- if (matches.length === 0) return css;
42
169
  try {
170
+ const dependencies = /* @__PURE__ */ new Set();
43
171
  const resolvedValues = await Promise.all(
44
- matches.map(async ({ moduleSpecifier, specifier }) => {
45
- const parsedModule = await parseModule(
46
- loader,
47
- moduleSpecifier,
48
- pathContext
172
+ yakImports.map(async ({ moduleSpecifier, specifier }) => {
173
+ const { resolved: resolvedModule } = await resolveModule(
174
+ context,
175
+ moduleSpecifier
49
176
  );
50
177
  const resolvedValue = await resolveModuleSpecifierRecursively(
51
- loader,
52
- parsedModule,
178
+ context,
179
+ resolvedModule,
53
180
  specifier
54
181
  );
182
+ for (const dependency of resolvedValue.from) {
183
+ dependencies.add(dependency);
184
+ }
55
185
  return resolvedValue;
56
186
  })
57
187
  );
58
188
  let result = css;
59
- for (let i = matches.length - 1; i >= 0; i--) {
60
- const { position, size, importKind, specifier, semicolon } = matches[i];
189
+ for (let i = yakImports.length - 1; i >= 0; i--) {
190
+ const { position, size, importKind, specifier, semicolon } = yakImports[i];
61
191
  const resolved = resolvedValues[i];
62
- if (importKind === "selector") {
63
- if (resolved.type !== "styled-component" && resolved.type !== "constant") {
64
- throw new Error(
65
- `Found ${resolved.type} but expected a selector - did you forget a semicolon after \`${specifier.join(
66
- "."
67
- )}\`?`
68
- );
192
+ let replacement;
193
+ if (resolved.type === "unresolved-tag") {
194
+ replacement = importKind === "mixin" ? "" : "undefined";
195
+ } else {
196
+ if (importKind === "selector") {
197
+ if (resolved.type !== "styled-component" && resolved.type !== "constant") {
198
+ throw new Error(
199
+ `Found "${resolved.type}" but expected a selector - did you forget a semicolon after "${specifier.join(
200
+ "."
201
+ )}"?`
202
+ );
203
+ }
69
204
  }
205
+ replacement = resolved.type === "styled-component" ? resolved.value : resolved.value + // resolved.value can be of two different types:
206
+ // - mixin:
207
+ // ${mixinName};
208
+ // - constant:
209
+ // color: ${value};
210
+ // For mixins the semicolon is already included in the value
211
+ // but for constants it has to be added manually
212
+ (["}", ";"].includes(String(resolved.value).trimEnd().slice(-1)) ? "" : semicolon);
70
213
  }
71
- const replacement = resolved.type === "styled-component" ? resolved.value : resolved.value + // resolved.value can be of two different types:
72
- // - mixin:
73
- // ${mixinName};
74
- // - constant:
75
- // color: ${value};
76
- // For mixins the semicolon is already included in the value
77
- // but for constants it has to be added manually
78
- (["}", ";"].includes(String(resolved.value).trimEnd().slice(-1)) ? "" : semicolon);
79
214
  result = result.slice(0, position) + String(replacement) + result.slice(position + size);
80
215
  }
81
- return result;
216
+ return { resolved: result, dependencies: Array.from(dependencies) };
82
217
  } catch (error) {
83
- throw new Error(
84
- `Error resolving cross-file selectors: ${error.message}
85
- File: ${loader.resourcePath}`
218
+ throw new CauseError(
219
+ `Error while resolving cross-file selectors in file "${filePath}"`,
220
+ { cause: error }
86
221
  );
87
222
  }
88
223
  }
89
- async function resolveModule(loader, moduleSpecifier, context) {
90
- return new Promise((resolve, reject) => {
91
- loader.resolve(context, moduleSpecifier, (err, result) => {
92
- if (err) return reject(err);
93
- if (!result)
94
- return reject(new Error(`Could not resolve ${moduleSpecifier}`));
95
- resolve(result);
224
+ async function parseYakCssImport(context, filePath, css) {
225
+ const yakImports = [];
226
+ for (const match of css.matchAll(yakCssImportRegex)) {
227
+ const [fullMatch, encodedArguments, importKind, semicolon] = match;
228
+ const [moduleSpecifier, ...specifier] = encodedArguments.split(":").map((entry) => decodeURIComponent(entry));
229
+ yakImports.push({
230
+ encodedArguments,
231
+ moduleSpecifier: await context.resolve(moduleSpecifier, filePath),
232
+ specifier,
233
+ importKind,
234
+ semicolon,
235
+ position: match.index,
236
+ size: fullMatch.length
96
237
  });
97
- });
238
+ }
239
+ return yakImports;
98
240
  }
99
- async function parseModule(loader, moduleSpecifier, context) {
100
- const cache = getCompilationCache(loader).parsedFiles;
101
- const resolvedModule = await resolveModule(loader, moduleSpecifier, context);
102
- let parsedFile = cache.get(resolvedModule);
103
- if (!parsedFile) {
104
- parsedFile = await parseFile(loader, resolvedModule);
105
- cache.set(resolvedModule, parsedFile);
106
- }
107
- loader.addDependency(parsedFile.filePath);
108
- return parsedFile;
241
+ async function resolveModule(context, filePath) {
242
+ if (context.cache?.resolve === void 0) {
243
+ return uncachedResolveModule(context, filePath);
244
+ }
245
+ const cached = context.cache.resolve.get(filePath);
246
+ if (cached === void 0) {
247
+ const resolvedPromise = uncachedResolveModule(context, filePath);
248
+ context.cache.resolve.set(filePath, resolvedPromise);
249
+ if (context.cache.resolve.addDependency) {
250
+ context.cache.resolve.addDependency(filePath, filePath);
251
+ resolvedPromise.then((value) => {
252
+ for (const dep of value.dependencies) {
253
+ context.cache.resolve.addDependency(filePath, dep);
254
+ }
255
+ });
256
+ }
257
+ return resolvedPromise;
258
+ }
259
+ return cached;
109
260
  }
110
- async function parseFile(loader, filePath) {
111
- const isYak = filePath.endsWith(".yak.ts") || filePath.endsWith(".yak.tsx") || filePath.endsWith(".yak.js") || filePath.endsWith(".yak.jsx");
112
- try {
113
- if (isYak) {
114
- const module = await loader.importModule(filePath);
115
- const mappedModule = Object.fromEntries(
116
- Object.entries(module).map(([key, value]) => {
117
- if (typeof value === "string" || typeof value === "number") {
118
- return [key, { type: "constant", value }];
119
- } else if (value && (typeof value === "object" || Array.isArray(value))) {
120
- return [key, { type: "record", value }];
121
- } else {
122
- return [key, { type: "unsupported", hint: String(value) }];
261
+ async function uncachedResolveModule(context, filePath) {
262
+ const parsedModule = await context.parse(filePath);
263
+ const exports = parsedModule.exports;
264
+ if (parsedModule.type !== "regular") {
265
+ return {
266
+ resolved: {
267
+ path: parsedModule.path,
268
+ exports
269
+ },
270
+ dependencies: []
271
+ };
272
+ }
273
+ const dependencies = /* @__PURE__ */ new Set();
274
+ if (parsedModule.styledComponents) {
275
+ Object.values(parsedModule.styledComponents).map((styledComponent) => {
276
+ if (styledComponent.nameParts.length === 1) {
277
+ exports.named[styledComponent.nameParts[0]] = {
278
+ type: "styled-component",
279
+ className: styledComponent.value
280
+ };
281
+ } else {
282
+ let exportEntry = exports.named[styledComponent.nameParts[0]];
283
+ if (!exportEntry) {
284
+ exportEntry = { type: "record", value: {} };
285
+ exports.named[styledComponent.nameParts[0]] = exportEntry;
286
+ } else if (exportEntry.type !== "record") {
287
+ throw new CauseError(`Error parsing file "${parsedModule.path}"`, {
288
+ cause: `"${styledComponent.nameParts[0]}" is not a record`
289
+ });
290
+ }
291
+ let current = exportEntry.value;
292
+ for (let i = 1; i < styledComponent.nameParts.length - 1; i++) {
293
+ let next = current[styledComponent.nameParts[i]];
294
+ if (!next) {
295
+ next = { type: "record", value: {} };
296
+ current[styledComponent.nameParts[i]] = next;
297
+ } else if (next.type !== "record") {
298
+ throw new CauseError(`Error parsing file "${parsedModule.path}"`, {
299
+ cause: `"${styledComponent.nameParts.slice(0, i + 1).join(".")}" is not a record`
300
+ });
123
301
  }
124
- })
125
- );
126
- return { type: "yak", exports: mappedModule, filePath };
127
- }
128
- const sourceContents = new Promise(
129
- (resolve, reject) => loader.fs.readFile(filePath, "utf-8", (err, result) => {
130
- if (err) return reject(err);
131
- resolve(result || "");
132
- })
133
- );
134
- const tranformedSource = new Promise((resolve, reject) => {
135
- loader.loadModule(filePath, (err, source) => {
136
- if (err) return reject(err);
137
- let sourceString;
138
- if (typeof source === "string") {
139
- sourceString = source;
140
- } else if (source instanceof Buffer) {
141
- sourceString = source.toString("utf-8");
142
- } else if (source instanceof ArrayBuffer) {
143
- sourceString = new TextDecoder("utf-8").decode(source);
144
- } else {
145
- throw new Error(
146
- "Invalid input type: code must be string, Buffer, or ArrayBuffer"
147
- );
302
+ current = next.value;
148
303
  }
149
- resolve(sourceString || "");
150
- });
304
+ current[styledComponent.nameParts[styledComponent.nameParts.length - 1]] = {
305
+ type: "styled-component",
306
+ className: styledComponent.value
307
+ };
308
+ }
151
309
  });
152
- const exports = await parseExports(await sourceContents);
153
- const mixins = parseMixins(await tranformedSource);
154
- Object.assign(
155
- exports,
156
- parseStyledComponents(
157
- await tranformedSource,
158
- loader.getOptions().experiments?.transpilationMode
159
- )
160
- );
310
+ }
311
+ if (parsedModule.mixins) {
161
312
  await Promise.all(
162
- Object.entries(mixins).map(async ([name, { value, nameParts }]) => {
163
- const resolvedValue = await resolveCrossFileConstant(
164
- loader,
165
- path.dirname(filePath),
166
- value
313
+ Object.values(parsedModule.mixins).map(async (mixin) => {
314
+ const { resolved, dependencies: deps } = await resolveCrossFileConstant(
315
+ context,
316
+ parsedModule.path,
317
+ mixin.value
167
318
  );
168
- if (nameParts.length === 1) {
169
- exports[name] = { type: "mixin", value: resolvedValue };
319
+ for (const dep of deps) {
320
+ dependencies.add(dep);
321
+ }
322
+ if (mixin.nameParts.length === 1) {
323
+ exports.named[mixin.nameParts[0]] = {
324
+ type: "mixin",
325
+ value: resolved
326
+ };
170
327
  } else {
171
- let exportEntry = exports[nameParts[0]];
328
+ let exportEntry = exports.named[mixin.nameParts[0]];
172
329
  if (!exportEntry) {
173
330
  exportEntry = { type: "record", value: {} };
174
- exports[nameParts[0]] = exportEntry;
331
+ exports.named[mixin.nameParts[0]] = exportEntry;
175
332
  } else if (exportEntry.type !== "record") {
176
- throw new Error(
177
- `Error parsing file ${filePath}: ${nameParts[0]} is not a record`
178
- );
333
+ throw new CauseError(`Error parsing file "${parsedModule.path}"`, {
334
+ cause: `"${mixin.nameParts[0]}" is not a record`
335
+ });
179
336
  }
180
337
  let current = exportEntry.value;
181
- for (let i = 1; i < nameParts.length - 1; i++) {
182
- let next = current[nameParts[i]];
338
+ for (let i = 1; i < mixin.nameParts.length - 1; i++) {
339
+ let next = current[mixin.nameParts[i]];
183
340
  if (!next) {
184
341
  next = { type: "record", value: {} };
185
- current[nameParts[i]] = next;
342
+ current[mixin.nameParts[i]] = next;
186
343
  } else if (next.type !== "record") {
187
- throw new Error(
188
- `Error parsing file ${filePath}: ${nameParts[i]} is not a record`
344
+ throw new CauseError(
345
+ `Error parsing file "${parsedModule.path}"`,
346
+ {
347
+ cause: `"${mixin.nameParts.slice(0, i + 1).join(".")}" is not a record`
348
+ }
189
349
  );
190
350
  }
191
351
  current = next.value;
192
352
  }
193
- current[nameParts[nameParts.length - 1]] = {
353
+ current[mixin.nameParts[mixin.nameParts.length - 1]] = {
194
354
  type: "mixin",
195
- value: resolvedValue
355
+ value: resolved
196
356
  };
197
357
  }
198
358
  })
199
359
  );
200
- return {
201
- type: "regular",
202
- exports,
203
- filePath
204
- };
360
+ }
361
+ return {
362
+ resolved: {
363
+ path: parsedModule.path,
364
+ exports
365
+ },
366
+ dependencies: Array.from(dependencies)
367
+ };
368
+ }
369
+ async function resolveModuleSpecifierRecursively(context, resolvedModule, specifiers, seen = /* @__PURE__ */ new Set()) {
370
+ const exportName = specifiers[0];
371
+ const exportValue = resolvedModule.exports.named[exportName];
372
+ if (exportValue !== void 0) {
373
+ if (seen.has(resolvedModule.path + ":" + exportName)) {
374
+ throw new CircularDependencyError(
375
+ `Unable to resolve "${specifiers.join(".")}" in module "${resolvedModule.path}"`,
376
+ { cause: "Circular dependency detected" }
377
+ );
378
+ }
379
+ seen.add(resolvedModule.path + ":" + exportName);
380
+ return resolveModuleExport(
381
+ context,
382
+ resolvedModule.path,
383
+ exportValue,
384
+ specifiers,
385
+ seen
386
+ );
387
+ }
388
+ let i = 1;
389
+ for (const from of resolvedModule.exports.all) {
390
+ if (context.exportAllLimit && i++ > context.exportAllLimit) {
391
+ throw new ResolveError(
392
+ `Unable to resolve "${specifiers.join(".")}" in module "${resolvedModule.path}"`,
393
+ {
394
+ cause: `More than ${context.exportAllLimit} star exports are not supported for performance reasons`
395
+ }
396
+ );
397
+ }
398
+ try {
399
+ const resolved = await resolveModuleExport(
400
+ context,
401
+ resolvedModule.path,
402
+ {
403
+ type: "re-export",
404
+ from,
405
+ name: exportName
406
+ },
407
+ specifiers,
408
+ seen
409
+ );
410
+ if (seen.has(resolvedModule.path + ":*")) {
411
+ throw new CircularDependencyError(
412
+ `Unable to resolve "${specifiers.join(".")}" in module "${resolvedModule.path}"`,
413
+ { cause: "Circular dependency detected" }
414
+ );
415
+ }
416
+ seen.add(resolvedModule.path + ":*");
417
+ return resolved;
418
+ } catch (error) {
419
+ if (!(error instanceof ResolveError)) {
420
+ throw error;
421
+ }
422
+ if (error.circular) {
423
+ throw error;
424
+ }
425
+ }
426
+ }
427
+ throw new ResolveError(`Unable to resolve "${specifiers.join(".")}"`, {
428
+ cause: `no matching export found in module "${resolvedModule.path}"`
429
+ });
430
+ }
431
+ async function resolveModuleExport(context, filePath, moduleExport, specifiers, seen) {
432
+ try {
433
+ switch (moduleExport.type) {
434
+ case "re-export": {
435
+ const { resolved: reExportedModule } = await resolveModule(
436
+ context,
437
+ await context.resolve(moduleExport.from, filePath)
438
+ );
439
+ const resolved = await resolveModuleSpecifierRecursively(
440
+ context,
441
+ reExportedModule,
442
+ [moduleExport.name, ...specifiers.slice(1)],
443
+ seen
444
+ );
445
+ if (resolved) {
446
+ resolved.from.push(filePath);
447
+ }
448
+ return resolved;
449
+ }
450
+ case "namespace-re-export": {
451
+ const { resolved: reExportedModule } = await resolveModule(
452
+ context,
453
+ await context.resolve(moduleExport.from, filePath)
454
+ );
455
+ const resolved = await resolveModuleSpecifierRecursively(
456
+ context,
457
+ reExportedModule,
458
+ specifiers.slice(1),
459
+ seen
460
+ );
461
+ if (resolved) {
462
+ resolved.from.push(filePath);
463
+ }
464
+ return resolved;
465
+ }
466
+ case "styled-component": {
467
+ return {
468
+ type: "styled-component",
469
+ from: [filePath],
470
+ source: filePath,
471
+ name: specifiers[specifiers.length - 1],
472
+ value: moduleExport.className
473
+ };
474
+ }
475
+ // usually at this point `tag-template` exports where already resolved to
476
+ // styled-components if a matching styled-component comment was generated
477
+ // by yak-swc. So resolving a value to a `tag-template` at this stage
478
+ // would mean that the user tried to use the result of a call to a
479
+ // different tag-template than yak's styled in a template. This is usually
480
+ // invalid.
481
+ //
482
+ // But there is an issue with Nextjs. Next build in two passes, once for
483
+ // the server bundle, once for the client bundle. During the server-side
484
+ // build, each module with the `"use client"` directive is transformed to
485
+ // throw errors if the exported symbol are used. This transformation
486
+ // removes the comments generated by `yak-swc`, so instead of the expected
487
+ // `styled-component`, calls to `styled` resolve to a `tag-template`
488
+ // (because no classname was found in the now absent comments).
489
+ //
490
+ // To summarize, if a "use client" bundle exports a styled component that
491
+ // is used in a "standard" module, the resolve logic would throw with
492
+ // "unknown type tag-template".
493
+ //
494
+ // To avoid this error, the resolve logic must handle those `tag-template`
495
+ // with a special type `unresolved-tag`. Those will be interpolated to
496
+ // valid CSS with minimal effect (to avoid CSS syntax error in the case of
497
+ // Nextjs server build)
498
+ case "tag-template": {
499
+ return {
500
+ type: "unresolved-tag",
501
+ from: [filePath],
502
+ source: filePath,
503
+ name: specifiers[specifiers.length - 1]
504
+ };
505
+ }
506
+ case "constant": {
507
+ return {
508
+ type: "constant",
509
+ from: [filePath],
510
+ source: filePath,
511
+ value: moduleExport.value
512
+ };
513
+ }
514
+ case "record": {
515
+ const resolvedInRecord = resolveSpecifierInRecord(
516
+ moduleExport,
517
+ specifiers[0],
518
+ specifiers.slice(1)
519
+ );
520
+ return resolveModuleExport(
521
+ context,
522
+ filePath,
523
+ resolvedInRecord,
524
+ specifiers,
525
+ seen
526
+ );
527
+ }
528
+ case "mixin": {
529
+ return {
530
+ type: "mixin",
531
+ from: [filePath],
532
+ source: filePath,
533
+ value: moduleExport.value
534
+ };
535
+ }
536
+ }
205
537
  } catch (error) {
206
- throw new Error(
207
- `Error parsing file ${filePath}: ${error.message}`
538
+ throw new ResolveError(
539
+ `Unable to resolve "${specifiers.join(".")}" in module "${filePath}"`,
540
+ { cause: error }
541
+ );
542
+ }
543
+ throw new ResolveError(
544
+ `Unable to resolve "${specifiers.join(".")}" in module "${filePath}"`,
545
+ { cause: `unknown type "${moduleExport.type}"` }
546
+ );
547
+ }
548
+ function resolveSpecifierInRecord(record, name, specifiers) {
549
+ if (specifiers.length === 0) {
550
+ throw new ResolveError("did not expect an object");
551
+ }
552
+ let depth = 0;
553
+ let current = record;
554
+ while (current && current.type === "record" && depth < specifiers.length) {
555
+ current = current.value[specifiers[depth]];
556
+ depth += 1;
557
+ }
558
+ if (current === void 0 || depth !== specifiers.length) {
559
+ throw new ResolveError(
560
+ `Unable to resolve "${specifiers.join(".")}" in object/array "${name}"`,
561
+ { cause: "path not found" }
208
562
  );
209
563
  }
564
+ if (current.type === "constant" || current.type === "styled-component" || current.type === "mixin") {
565
+ return current;
566
+ }
567
+ if (current.type === "record" && "__yak" in current.value && current.value.__yak.type === "constant") {
568
+ return { type: "mixin", value: String(current.value.__yak.value) };
569
+ }
570
+ throw new ResolveError(
571
+ `Unable to resolve "${specifiers.join(".")}" in object/array "${name}"`,
572
+ { cause: "only string and numbers are supported" }
573
+ );
574
+ }
575
+ async function sha1(message) {
576
+ const resultBuffer = await globalThis.crypto.subtle.digest(
577
+ "SHA-1",
578
+ new TextEncoder().encode(message)
579
+ );
580
+ return Array.from(
581
+ new Uint8Array(resultBuffer),
582
+ (byte) => byte.toString(16).padStart(2, "0")
583
+ ).join("");
584
+ }
585
+
586
+ // loaders/lib/resolveCrossFileSelectors.ts
587
+ import { parse } from "@babel/parser";
588
+ import traverse from "@babel/traverse";
589
+ var compilationCache = /* @__PURE__ */ new WeakMap();
590
+ async function resolveCrossFileConstant2(loader, pathContext, css) {
591
+ const { resolved } = await resolveCrossFileConstant(
592
+ getResolveContext(loader),
593
+ loader.resourcePath,
594
+ css
595
+ );
596
+ return resolved;
597
+ }
598
+ function getCompilationCache(loader) {
599
+ const compilation = loader._compilation;
600
+ if (!compilation) {
601
+ throw new Error("Webpack compilation object not available");
602
+ }
603
+ let cache = compilationCache.get(compilation);
604
+ if (!cache) {
605
+ cache = {
606
+ parsedFiles: /* @__PURE__ */ new Map()
607
+ };
608
+ compilationCache.set(compilation, cache);
609
+ }
610
+ return cache;
611
+ }
612
+ function getParseContext(loader) {
613
+ return {
614
+ cache: { parse: getCompilationCache(loader).parsedFiles },
615
+ async extractExports(modulePath) {
616
+ const sourceContents = new Promise(
617
+ (resolve, reject) => loader.fs.readFile(modulePath, "utf-8", (err, result) => {
618
+ if (err) return reject(err);
619
+ resolve(result || "");
620
+ })
621
+ );
622
+ return parseExports(await sourceContents);
623
+ },
624
+ async getTransformed(modulePath) {
625
+ const tranformedSource = new Promise((resolve, reject) => {
626
+ loader.loadModule(modulePath, (err, source) => {
627
+ if (err) return reject(err);
628
+ let sourceString;
629
+ if (typeof source === "string") {
630
+ sourceString = source;
631
+ } else if (source instanceof Buffer) {
632
+ sourceString = source.toString("utf-8");
633
+ } else if (source instanceof ArrayBuffer) {
634
+ sourceString = new TextDecoder("utf-8").decode(source);
635
+ } else {
636
+ throw new Error(
637
+ "Invalid input type: code must be string, Buffer, or ArrayBuffer"
638
+ );
639
+ }
640
+ resolve(sourceString || "");
641
+ });
642
+ });
643
+ return { code: await tranformedSource };
644
+ },
645
+ async evaluateYakModule(modulePath) {
646
+ return loader.importModule(modulePath);
647
+ }
648
+ };
649
+ }
650
+ function getResolveContext(loader) {
651
+ const parseContext = getParseContext(loader);
652
+ return {
653
+ parse: (modulePath) => parseModule(parseContext, modulePath),
654
+ resolve: async (specifier, importer) => {
655
+ return resolveModule2(loader, specifier, dirname(importer));
656
+ }
657
+ };
658
+ }
659
+ async function resolveModule2(loader, moduleSpecifier, context) {
660
+ return new Promise((resolve, reject) => {
661
+ loader.resolve(context, moduleSpecifier, (err, result) => {
662
+ if (err) return reject(err);
663
+ if (!result)
664
+ return reject(new Error(`Could not resolve ${moduleSpecifier}`));
665
+ resolve(result);
666
+ });
667
+ });
210
668
  }
211
669
  async function parseExports(sourceContents) {
212
- let exports = {};
670
+ const moduleExports = {
671
+ importYak: true,
672
+ named: {},
673
+ all: []
674
+ };
213
675
  const variableDeclarations = {};
214
676
  let defaultIdentifier = null;
215
677
  try {
@@ -228,10 +690,10 @@ async function parseExports(sourceContents) {
228
690
  if (node.source) {
229
691
  node.specifiers.forEach((specifier) => {
230
692
  if (specifier.type === "ExportSpecifier" && specifier.exported.type === "Identifier" && specifier.local.type === "Identifier") {
231
- exports[specifier.exported.name] = {
693
+ moduleExports.named[specifier.exported.name] = {
232
694
  type: "re-export",
233
695
  from: node.source.value,
234
- imported: specifier.local.name
696
+ name: specifier.local.name
235
697
  };
236
698
  }
237
699
  });
@@ -240,7 +702,7 @@ async function parseExports(sourceContents) {
240
702
  if (declaration.id.type === "Identifier" && declaration.init) {
241
703
  const parsed = parseExportValueExpression(declaration.init);
242
704
  if (parsed) {
243
- exports[declaration.id.name] = parsed;
705
+ moduleExports.named[declaration.id.name] = parsed;
244
706
  }
245
707
  }
246
708
  });
@@ -251,9 +713,9 @@ async function parseExports(sourceContents) {
251
713
  const { specifiers, source } = node;
252
714
  specifiers.forEach((specifier) => {
253
715
  if (specifier.type === "ExportNamespaceSpecifier" && specifier.exported.type === "Identifier") {
254
- exports[specifier.exported.name] = {
255
- type: "star-export",
256
- from: [source.value]
716
+ moduleExports.named[specifier.exported.name] = {
717
+ type: "namespace-re-export",
718
+ from: source.value
257
719
  };
258
720
  }
259
721
  });
@@ -263,68 +725,30 @@ async function parseExports(sourceContents) {
263
725
  if (node.declaration.type === "Identifier") {
264
726
  defaultIdentifier = node.declaration.name;
265
727
  } else if (node.declaration.type === "FunctionDeclaration" || node.declaration.type === "ClassDeclaration") {
266
- exports["default"] = {
728
+ moduleExports.named["default"] = {
267
729
  type: "unsupported",
268
730
  hint: node.declaration.type
269
731
  };
270
732
  } else {
271
- exports["default"] = parseExportValueExpression(
733
+ moduleExports.named["default"] = parseExportValueExpression(
272
734
  node.declaration
273
735
  );
274
736
  }
275
737
  },
276
738
  ExportAllDeclaration({ node }) {
277
- if (Object.keys(exports).length === 0) {
278
- exports["*"] ||= {
279
- type: "star-export",
280
- from: []
281
- };
282
- if (exports["*"].type !== "star-export") {
283
- throw new Error("Invalid star export state");
284
- }
285
- exports["*"].from.push(node.source.value);
286
- }
739
+ moduleExports.all.push(node.source.value);
287
740
  }
288
741
  });
289
742
  if (defaultIdentifier && variableDeclarations[defaultIdentifier]) {
290
- exports["default"] = parseExportValueExpression(
743
+ moduleExports.named["default"] = parseExportValueExpression(
291
744
  variableDeclarations[defaultIdentifier]
292
745
  );
293
746
  }
294
- return exports;
747
+ return moduleExports;
295
748
  } catch (error) {
296
749
  throw new Error(`Error parsing exports: ${error.message}`);
297
750
  }
298
751
  }
299
- function parseMixins(sourceContents) {
300
- const mixinParts = sourceContents.split("/*YAK EXPORTED MIXIN:");
301
- let mixins = {};
302
- for (let i = 1; i < mixinParts.length; i++) {
303
- const [comment] = mixinParts[i].split("*/", 1);
304
- const position = comment.indexOf("\n");
305
- const name = comment.slice(0, position);
306
- const value = comment.slice(position + 1);
307
- mixins[name] = {
308
- type: "mixin",
309
- value,
310
- nameParts: name.split(":").map((part) => decodeURIComponent(part))
311
- };
312
- }
313
- return mixins;
314
- }
315
- function parseStyledComponents(sourceContents, transpilationMode) {
316
- const styledParts = sourceContents.split("/*YAK EXPORTED STYLED:");
317
- let styledComponents = {};
318
- for (let i = 1; i < styledParts.length; i++) {
319
- const [comment] = styledParts[i].split("*/", 1);
320
- const [componentName, className] = comment.split(":");
321
- styledComponents[componentName] = {
322
- type: "styled-component",
323
- value: transpilationMode === "Css" ? `.${className}` : `:global(.${className})`
324
- };
325
- }
326
- return styledComponents;
327
- }
328
752
  function unpackTSAsExpression(node) {
329
753
  if (node.type === "TSAsExpression") {
330
754
  return unpackTSAsExpression(node.expression);
@@ -334,7 +758,7 @@ function unpackTSAsExpression(node) {
334
758
  function parseExportValueExpression(node) {
335
759
  const expression = unpackTSAsExpression(node);
336
760
  if (expression.type === "CallExpression" || expression.type === "TaggedTemplateExpression") {
337
- return { type: "styled-component", value: void 0 };
761
+ return { type: "tag-template" };
338
762
  } else if (expression.type === "StringLiteral" || expression.type === "NumericLiteral") {
339
763
  return { type: "constant", value: expression.value };
340
764
  } else if (expression.type === "UnaryExpression" && expression.operator === "-" && expression.argument.type === "NumericLiteral") {
@@ -361,106 +785,17 @@ function parseObjectExpression(node) {
361
785
  }
362
786
  return result;
363
787
  }
364
- async function resolveModuleSpecifierRecursively(loader, module, specifier) {
365
- try {
366
- const exportName = specifier[0];
367
- let exportValue = module.exports[exportName];
368
- if (exportValue === void 0) {
369
- const starExport = module.exports["*"];
370
- if (starExport?.type === "star-export") {
371
- if (starExport.from.length > 1) {
372
- throw new Error(
373
- `Could not resolve ${specifier.join(".")} in module ${module.filePath} - Multiple star exports are not supported for performance reasons`
374
- );
375
- }
376
- exportValue = {
377
- type: "re-export",
378
- from: starExport.from[0],
379
- imported: exportName
380
- };
381
- } else {
382
- throw new Error(
383
- `Could not resolve "${specifier.join(".")}" in module ${module.filePath}`
384
- );
385
- }
386
- }
387
- if (exportValue.type === "re-export") {
388
- const importedModule = await parseModule(
389
- loader,
390
- exportValue.from,
391
- path.dirname(module.filePath)
392
- );
393
- return resolveModuleSpecifierRecursively(loader, importedModule, [
394
- exportValue.imported,
395
- ...specifier.slice(1)
396
- ]);
397
- } else if (exportValue.type === "star-export") {
398
- const importedModule = await parseModule(
399
- loader,
400
- exportValue.from[0],
401
- path.dirname(module.filePath)
402
- );
403
- return resolveModuleSpecifierRecursively(
404
- loader,
405
- importedModule,
406
- specifier.slice(1)
407
- );
408
- }
409
- if (exportValue.type === "styled-component") {
410
- return {
411
- type: "styled-component",
412
- from: module.filePath,
413
- name: specifier[specifier.length - 1],
414
- value: exportValue.value
415
- };
416
- } else if (exportValue.type === "constant") {
417
- return { type: "constant", value: exportValue.value };
418
- } else if (exportValue.type === "record") {
419
- let current = exportValue.value;
420
- let depth = 0;
421
- do {
422
- if (typeof current === "string" || typeof current === "number") {
423
- return {
424
- type: "constant",
425
- value: current
426
- };
427
- } else if (!current || typeof current !== "object" && !Array.isArray(current)) {
428
- throw new Error(
429
- `Error unpacking Record/Array "${exportName}".
430
- Key "${specifier[depth]}" was of type "${typeof current}" but only String and Number are supported`
431
- );
432
- }
433
- depth++;
434
- if (depth === specifier.length && "__yak" in current) {
435
- return { type: "mixin", value: current["__yak"] };
436
- } else if (depth === specifier.length && "value" in current) {
437
- return { type: "constant", value: current["value"] };
438
- } else if ("value" in current) {
439
- current = current.value[specifier[depth]];
440
- } else {
441
- current = current[specifier[depth]];
442
- }
443
- } while (current);
444
- if (specifier[depth] === void 0) {
445
- throw new Error(
446
- `Error unpacking Record/Array - could not extract \`${specifier.slice(0, depth).join(".")}\` is not a string or number`
447
- );
448
- }
449
- throw new Error(
450
- `Error unpacking Record/Array - could not extract \`${specifier[depth]}\` from \`${specifier.slice(0, depth).join(".")}\``
451
- );
452
- } else if (exportValue.type === "mixin") {
453
- return { type: "mixin", value: exportValue.value };
454
- }
455
- throw new Error(
456
- `Error unpacking Record/Array - unexpected exportValue "${exportValue.type}" for specifier "${specifier.join(".")}"`
457
- );
458
- } catch (error) {
459
- throw new Error(
460
- `Error resolving from module ${module.filePath}: ${error.message}
461
- Extracted values: ${JSON.stringify(module.exports, null, 2)}`
462
- );
788
+ var DIRNAME_POSIX_REGEX = /^((?:\.(?![^\/]))|(?:(?:\/?|)(?:[\s\S]*?)))(?:\/+?|)(?:(?:\.{1,2}|[^\/]+?|)(?:\.[^.\/]*|))(?:[\/]*)$/;
789
+ var DIRNAME_WIN32_REGEX = /^((?:\.(?![^\\]))|(?:(?:\\?|)(?:[\s\S]*?)))(?:\\+?|)(?:(?:\.{1,2}|[^\\]+?|)(?:\.[^.\\]*|))(?:[\\]*)$/;
790
+ function dirname(path) {
791
+ let dirname2 = DIRNAME_POSIX_REGEX.exec(path)?.[1];
792
+ if (!dirname2) {
793
+ dirname2 = DIRNAME_WIN32_REGEX.exec(path)?.[1];
794
+ }
795
+ if (!dirname2) {
796
+ throw new Error(`Can't extract dirname from ${path}`);
463
797
  }
798
+ return dirname2;
464
799
  }
465
800
 
466
801
  // loaders/css-loader.ts
@@ -480,7 +815,7 @@ async function cssExtractLoader(_code, sourceMap) {
480
815
  debugLog("ts", source);
481
816
  const css = extractCss(source, experiments?.transpilationMode);
482
817
  debugLog("css", css);
483
- return resolveCrossFileConstant(this, this.context, css).then((result) => {
818
+ return resolveCrossFileConstant2(this, this.context, css).then((result) => {
484
819
  debugLog("css resolved", css);
485
820
  return callback(null, result, sourceMap);
486
821
  }, callback);