@yak/react 0.0.0-stage → 10.0.0-rc.1

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 (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +114 -2
  3. package/dist/context/baseContext.d.ts +5 -0
  4. package/dist/context/baseContext.js +8 -0
  5. package/dist/context/baseContext.js.map +1 -0
  6. package/dist/context/index.d.ts +20 -0
  7. package/dist/context/index.js +15 -0
  8. package/dist/context/index.js.map +1 -0
  9. package/dist/context/index.server.d.ts +9 -0
  10. package/dist/context/index.server.js +17 -0
  11. package/dist/context/index.server.js.map +1 -0
  12. package/dist/cross-file-resolver/index.d.ts +199 -0
  13. package/dist/cross-file-resolver/index.js +569 -0
  14. package/dist/cross-file-resolver/index.js.map +1 -0
  15. package/dist/index.d.ts +320 -0
  16. package/dist/index.js +2 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/internal.d.ts +452 -0
  19. package/dist/internal.js +363 -0
  20. package/dist/internal.js.map +1 -0
  21. package/dist/isolated-source-eval/index.d.ts +30 -0
  22. package/dist/isolated-source-eval/index.js +317 -0
  23. package/dist/isolated-source-eval/index.js.map +1 -0
  24. package/dist/isolated-source-eval/worker.js +59 -0
  25. package/dist/isolated-source-eval/worker.js.map +1 -0
  26. package/dist/jsx-dev-runtime.d.ts +7 -0
  27. package/dist/jsx-dev-runtime.js +2 -0
  28. package/dist/jsx-dev-runtime.js.map +1 -0
  29. package/dist/jsx-runtime.d.ts +38 -0
  30. package/dist/jsx-runtime.js +2 -0
  31. package/dist/jsx-runtime.js.map +1 -0
  32. package/dist/loaders/turbo-loader.d.ts +15 -0
  33. package/dist/loaders/turbo-loader.js +816 -0
  34. package/dist/loaders/turbo-loader.js.map +1 -0
  35. package/dist/loaders/vite-plugin.d.ts +103 -0
  36. package/dist/loaders/vite-plugin.js +885 -0
  37. package/dist/loaders/vite-plugin.js.map +1 -0
  38. package/dist/loaders/webpack-loader.d.ts +12 -0
  39. package/dist/loaders/webpack-loader.js +710 -0
  40. package/dist/loaders/webpack-loader.js.map +1 -0
  41. package/dist/rsbuild/index.d.ts +29 -0
  42. package/dist/rsbuild/index.js +51 -0
  43. package/dist/rsbuild/index.js.map +1 -0
  44. package/dist/static/chunk-DK3Fl9T5.js +1 -0
  45. package/dist/static/index.d.ts +28 -0
  46. package/dist/static/index.js +2 -0
  47. package/dist/static/index.js.map +1 -0
  48. package/dist/withYak/index.d.ts +112 -0
  49. package/dist/withYak/index.js +171 -0
  50. package/dist/withYak/index.js.map +1 -0
  51. package/loaders/lib/resolveCrossFileSelectors.ts +161 -0
  52. package/loaders/turbo-evaluator.ts +134 -0
  53. package/loaders/turbo-loader.ts +184 -0
  54. package/loaders/vite-plugin.ts +28 -0
  55. package/loaders/webpack-loader.ts +41 -0
  56. package/package.json +155 -3
  57. package/rsbuild/index.ts +91 -0
  58. package/runtime/__tests__/atoms.test.tsx +48 -0
  59. package/runtime/__tests__/attrs.test.tsx +490 -0
  60. package/runtime/__tests__/cssLiteral.test.tsx +503 -0
  61. package/runtime/__tests__/cssProp.test.tsx +392 -0
  62. package/runtime/__tests__/cssPropTest.tsx +227 -0
  63. package/runtime/__tests__/foldedJsxParity.test.tsx +117 -0
  64. package/runtime/__tests__/styled.test.tsx +653 -0
  65. package/runtime/__tests__/tsconfig.json +18 -0
  66. package/runtime/__tests__/typeTest.tsx +374 -0
  67. package/runtime/atoms.tsx +36 -0
  68. package/runtime/context/README.md +84 -0
  69. package/runtime/context/baseContext.tsx +5 -0
  70. package/runtime/context/index.server.tsx +22 -0
  71. package/runtime/context/index.tsx +36 -0
  72. package/runtime/cssLiteral.tsx +214 -0
  73. package/runtime/globalStyle.tsx +54 -0
  74. package/runtime/index.ts +37 -0
  75. package/runtime/internal.ts +42 -0
  76. package/runtime/internals/mergeClassNames.ts +23 -0
  77. package/runtime/internals/mergeCssProp.ts +69 -0
  78. package/runtime/internals/propMarkers.ts +20 -0
  79. package/runtime/internals/unitPostFix.ts +34 -0
  80. package/runtime/jsx-dev-runtime.ts +6 -0
  81. package/runtime/jsx-runtime.ts +48 -0
  82. package/runtime/keyframes.tsx +30 -0
  83. package/runtime/mocks/cssLiteral.ts +74 -0
  84. package/runtime/mocks/globalStyle.ts +14 -0
  85. package/runtime/mocks/keyframes.ts +29 -0
  86. package/runtime/mocks/styled.ts +8 -0
  87. package/runtime/publicStyledApi.ts +159 -0
  88. package/runtime/styled.tsx +372 -0
  89. package/runtime/styledDom.tsx +139 -0
  90. package/withYak/allow-global-css.ts +94 -0
  91. package/withYak/index.ts +268 -0
@@ -0,0 +1,161 @@
1
+ import type { Compilation, LoaderContext } from "webpack";
2
+ import {
3
+ ParseContext,
4
+ ParsedModule,
5
+ parseModule,
6
+ } from "yak-internals/cross-file-resolver/parse-module";
7
+ import {
8
+ ResolveContext,
9
+ resolveCrossFileConstant as genericResolveCrossFileConstant,
10
+ } from "yak-internals/cross-file-resolver/resolve";
11
+ import { parseExports } from "yak-internals/parse-exports";
12
+ import { YakConfigOptions } from "yak-internals/config";
13
+
14
+ export { parseExports };
15
+
16
+ const compilationCache = new WeakMap<
17
+ Compilation,
18
+ {
19
+ parsedFiles: Map<string, ParsedModule>;
20
+ }
21
+ >();
22
+
23
+ export async function resolveCrossFileConstant(
24
+ loader: LoaderContext<{}>,
25
+ pathContext: string,
26
+ css: string,
27
+ ): Promise<string> {
28
+ const { resolved } = await genericResolveCrossFileConstant(
29
+ getResolveContext(loader),
30
+ loader.resourcePath,
31
+ css,
32
+ );
33
+ return resolved;
34
+ }
35
+
36
+ function getCompilationCache(loader: LoaderContext<YakConfigOptions>) {
37
+ const compilation = loader._compilation;
38
+ if (!compilation) {
39
+ throw new Error("Webpack compilation object not available");
40
+ }
41
+ let cache = compilationCache.get(compilation);
42
+ if (!cache) {
43
+ cache = {
44
+ parsedFiles: new Map(),
45
+ };
46
+ compilationCache.set(compilation, cache);
47
+ }
48
+ return cache;
49
+ }
50
+
51
+ function getParseContext(loader: LoaderContext<YakConfigOptions>): ParseContext {
52
+ return {
53
+ cache: { parse: getCompilationCache(loader).parsedFiles },
54
+ async extractExports(modulePath) {
55
+ const sourceContents = new Promise<string>((resolve, reject) =>
56
+ loader.fs.readFile(modulePath, "utf-8", (err, result) => {
57
+ if (err) return reject(err);
58
+ resolve(result || "");
59
+ }),
60
+ );
61
+ return parseExports(await sourceContents);
62
+ },
63
+ async getTransformed(modulePath) {
64
+ const transformedSource = new Promise<string>((resolve, reject) => {
65
+ loader.loadModule(modulePath, (err, source) => {
66
+ if (err) {
67
+ // When webpack reports "The loaded module contains errors",
68
+ // the actual errors are stored on the module in the compilation.
69
+ // Extract and report the real errors for better debugging.
70
+ const compilation = loader._compilation;
71
+ if (compilation) {
72
+ try {
73
+ for (const mod of compilation.modules) {
74
+ if ("resource" in mod && mod.resource === modulePath) {
75
+ const errors = mod.getErrors();
76
+ if (errors) {
77
+ const messages = Array.from(errors)
78
+ .map((e) => e.message)
79
+ .filter(Boolean);
80
+ if (messages.length > 0) {
81
+ return reject(new Error(messages.join("\n")));
82
+ }
83
+ }
84
+ }
85
+ }
86
+ } catch {
87
+ // Ignore errors while trying to extract module errors
88
+ }
89
+ }
90
+ return reject(err);
91
+ }
92
+ let sourceString: string;
93
+ if (typeof source === "string") {
94
+ sourceString = source;
95
+ } else if (source instanceof Buffer) {
96
+ sourceString = source.toString("utf-8");
97
+ } else if (source instanceof ArrayBuffer) {
98
+ sourceString = new TextDecoder("utf-8").decode(source);
99
+ } else {
100
+ throw new Error("Invalid input type: code must be string, Buffer, or ArrayBuffer");
101
+ }
102
+ resolve(sourceString || "");
103
+ });
104
+ });
105
+ return { code: await transformedSource };
106
+ },
107
+ async evaluateYakModule(modulePath) {
108
+ return loader.importModule(modulePath);
109
+ },
110
+ };
111
+ }
112
+
113
+ function getResolveContext(loader: LoaderContext<YakConfigOptions>): ResolveContext {
114
+ const parseContext = getParseContext(loader);
115
+ return {
116
+ parse: (modulePath) => parseModule(parseContext, modulePath),
117
+ resolve: async (specifier, importer) => {
118
+ return resolveModule(loader, specifier, dirname(importer));
119
+ },
120
+ };
121
+ }
122
+
123
+ /**
124
+ * Resolves a module by wrapping loader.resolve in a promise
125
+ */
126
+ export async function resolveModule(
127
+ loader: LoaderContext<{}>,
128
+ moduleSpecifier: string,
129
+ context: string,
130
+ ): Promise<string> {
131
+ return new Promise<string>((resolve, reject) => {
132
+ loader.resolve(context, moduleSpecifier, (err, result) => {
133
+ if (err) return reject(err);
134
+ if (!result) return reject(new Error(`Could not resolve ${moduleSpecifier}`));
135
+ resolve(result);
136
+ });
137
+ });
138
+ }
139
+
140
+ const DIRNAME_POSIX_REGEX =
141
+ /^((?:\.(?![^/]))|(?:(?:\/?|)(?:[\s\S]*?)))(?:\/+?|)(?:(?:\.{1,2}|[^/]+?|)(?:\.[^./]*|))(?:[/]*)$/;
142
+ const DIRNAME_WIN32_REGEX =
143
+ /^((?:\.(?![^\\]))|(?:(?:\\?|)(?:[\s\S]*?)))(?:\\+?|)(?:(?:\.{1,2}|[^\\]+?|)(?:\.[^.\\]*|))(?:[\\]*)$/;
144
+
145
+ /**
146
+ * Polyfill for `node:path` method dirname.
147
+ * Keeps yak independent from node api (therefore executable in browser)
148
+ */
149
+ function dirname(path: string) {
150
+ let dirname = DIRNAME_POSIX_REGEX.exec(path)?.[1];
151
+
152
+ if (!dirname) {
153
+ dirname = DIRNAME_WIN32_REGEX.exec(path)?.[1];
154
+ }
155
+
156
+ if (!dirname) {
157
+ throw new Error(`Can't extract dirname from ${path}`);
158
+ }
159
+
160
+ return dirname;
161
+ }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Turbopack evaluator adapter.
3
+ *
4
+ * Bridges the low-level `Evaluator` (worker-thread module evaluator) and
5
+ * turbopack's loader lifecycle. A single `Evaluator` instance is kept as a
6
+ * singleton on `globalThis` because turbopack may create multiple loader
7
+ * instances that share the same process — `Symbol.for` keys guarantee all
8
+ * instances converge on the same evaluator and modification time map.
9
+ *
10
+ * Invalidation uses synchronous modification time polling rather than `fs.watch` because
11
+ * turbopack can re-run a loader before a filesystem watcher event fires,
12
+ * which would cause the evaluator to return stale cached results.
13
+ */
14
+ import { statSync } from "node:fs";
15
+ import type { Evaluator } from "../isolated-source-eval/index.js";
16
+
17
+ const EVALUATOR_KEY = Symbol.for("@yak/react-evaluator");
18
+ const MTIMES_KEY = Symbol.for("@yak/react-mtimes");
19
+
20
+ /**
21
+ * A shared `Map` of file paths to their last known modification times, used to detect changes on disk
22
+ *
23
+ * e.g. bar.ts -> 1680000000000 (Sat Mar 28 2024 12:00:00 GMT+0000)
24
+ *
25
+ * fs.fileWatch is not used as it might be out of sync with turbopacks own file watching also turbopack
26
+ * spawns a loader pool which could cause a lot of file watchers to be created which can cause performance issues and hitting OS limits
27
+ */
28
+ const getFileModificationTimes = () =>
29
+ ((globalThis as typeof globalThis & { [MTIMES_KEY]?: Map<string, number> })[MTIMES_KEY] ??=
30
+ new Map<string, number>());
31
+
32
+ /**
33
+ * Returns the shared evaluator, creating it on first call.
34
+ *
35
+ * Uses `Symbol.for` keys on `globalThis` so every turbopack loader instance
36
+ * in the process converges on the same evaluator and modification time map. A
37
+ * `beforeExit` handler terminates the worker threads when the process winds
38
+ * down.
39
+ */
40
+ export const getEvaluatorSingleton = async (): Promise<Evaluator> => {
41
+ const processGlobal = globalThis as typeof globalThis & {
42
+ [EVALUATOR_KEY]?: Promise<Evaluator>;
43
+ };
44
+ if (!processGlobal[EVALUATOR_KEY]) {
45
+ processGlobal[EVALUATOR_KEY] = import("../isolated-source-eval/index.js").then(
46
+ ({ createEvaluator }) => createEvaluator(),
47
+ );
48
+ // Kill the worker threads when the process exits
49
+ process.on("beforeExit", async () => {
50
+ const evaluator = await processGlobal[EVALUATOR_KEY]!;
51
+ await evaluator.dispose();
52
+ });
53
+ }
54
+ return processGlobal[EVALUATOR_KEY]!;
55
+ };
56
+
57
+ /**
58
+ * Turbopack runs the loader for the same compilation file-by-file, but multiple files can share the
59
+ * same dependency. This function detects which dependencies actually changed
60
+ * on disk and invalidates them once — this prevents re-evaluating the same module multiple times in the same compilation
61
+ */
62
+ function invalidateDependenciesWithDiskModifications(evaluator: Evaluator): void {
63
+ const modificationTimes = getFileModificationTimes();
64
+ const modifiedDependencies: string[] = [];
65
+ for (const [dep, lastModificationTime] of modificationTimes) {
66
+ try {
67
+ const currentModificationTime = statSync(dep).mtimeMs;
68
+ if (currentModificationTime !== lastModificationTime) {
69
+ modifiedDependencies.push(dep);
70
+ modificationTimes.set(dep, currentModificationTime);
71
+ }
72
+ } catch {
73
+ modifiedDependencies.push(dep);
74
+ modificationTimes.delete(dep);
75
+ }
76
+ }
77
+ if (modifiedDependencies.length > 0) {
78
+ evaluator.invalidate(...modifiedDependencies);
79
+ }
80
+ }
81
+
82
+ /** Snapshots the current modification times of `deps` so the next evaluation can detect changes. */
83
+ function recordModificationTimes(deps: string[]): void {
84
+ const modificationTimes = getFileModificationTimes();
85
+ for (const dep of deps) {
86
+ try {
87
+ modificationTimes.set(dep, statSync(dep).mtimeMs);
88
+ } catch {
89
+ /* file may not exist */
90
+ }
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Creates an evaluator function scoped to a single loader invocation.
96
+ *
97
+ * Before evaluation, checks whether any previously recorded dependency has
98
+ * changed on disk and invalidates the evaluator cache if so. After a
99
+ * successful evaluation, records fresh modification times and notifies the caller of
100
+ * every transitive dependency via `onDependency`.
101
+ *
102
+ * @returns A function that evaluates a yak module and returns its exports.
103
+ * The returned function accepts:
104
+ * - `modulePath` — absolute path to the module to evaluate.
105
+ * - `onDependency` — called once per transitive dependency so the loader
106
+ * can register file watchers with turbopack.
107
+ * @throws If the evaluated module throws at runtime — the original stack
108
+ * trace from the worker thread is preserved.
109
+ */
110
+ export async function createCompilationEvaluator(): Promise<
111
+ (modulePath: string, onDependency?: (dep: string) => void) => Promise<Record<string, unknown>>
112
+ > {
113
+ const evaluator = await getEvaluatorSingleton();
114
+ invalidateDependenciesWithDiskModifications(evaluator);
115
+
116
+ return async (modulePath, onDependency?) => {
117
+ const result = await evaluator.evaluate(modulePath);
118
+ if (!result.ok) {
119
+ const error = new Error(result.error.message);
120
+ if (result.error.stack) {
121
+ error.stack = result.error.stack;
122
+ }
123
+ throw error;
124
+ }
125
+ // Store times of newly discovered dependencies for next compilation
126
+ recordModificationTimes(result.dependencies);
127
+ if (onDependency) {
128
+ for (const dep of result.dependencies) {
129
+ onDependency(dep);
130
+ }
131
+ }
132
+ return result.value;
133
+ };
134
+ }
@@ -0,0 +1,184 @@
1
+ import { transform as swcTransform } from "@swc/core";
2
+ import { createRequire } from "node:module";
3
+ import { dirname } from "node:path";
4
+ import type { LoaderContext } from "webpack";
5
+ import { parseModule } from "yak-internals/cross-file-resolver/parse-module";
6
+ import { resolveCrossFileConstant } from "yak-internals/cross-file-resolver/resolve";
7
+ import type { YakConfigOptions } from "../withYak/index.js";
8
+ import { createDebugLogger } from "yak-internals/debug-logger";
9
+ import { extractCss } from "yak-internals/extract-css";
10
+ import { parseExports } from "yak-internals/parse-exports";
11
+ import { getSwcParserOptions } from "yak-internals/swc-parser-options";
12
+
13
+ const require = createRequire(import.meta.url);
14
+ const yakSwcPluginPath = require.resolve("@yak/swc");
15
+ const dataUrlImportPrefix = 'import "data:text/css;base64,';
16
+
17
+ /**
18
+ * This loader transforms styled-components styles to a static data-url import
19
+ * The compile-time nexy-yak transformation takes javascript/typescript as input,
20
+ * strips all inline css code and adds the css as static css urls
21
+ * e.g.: `import "data:text/css;base64,"`
22
+ */
23
+ export default async function cssExtractLoader(
24
+ this: LoaderContext<{ yakOptions: YakConfigOptions; yakPluginOptions: any }>,
25
+ code: string,
26
+ sourceMap: string | undefined,
27
+ ): Promise<string | void> {
28
+ const callback = this.async();
29
+
30
+ // process only files which include @yak/react for maximal compile performance
31
+ if (!code.includes("@yak/react")) {
32
+ return callback(null, code, sourceMap);
33
+ }
34
+
35
+ const {
36
+ yakPluginOptions,
37
+ yakOptions: { debug },
38
+ } = this.getOptions();
39
+ const debugLog = createDebugLogger(debug, this.rootContext);
40
+ const resolveTurbopack = this.getResolve({});
41
+ const transform = createTransform(yakPluginOptions, yakSwcPluginPath);
42
+
43
+ const resolveFn = (specifier: string, importer: string) => {
44
+ return new Promise<string>((resolve, reject) => {
45
+ resolveTurbopack(dirname(importer), specifier, (err, result) => {
46
+ if (err) return reject(err);
47
+ if (!result) return reject(new Error(`Could not resolve ${specifier}`));
48
+ resolve(result);
49
+ });
50
+ });
51
+ };
52
+
53
+ const crossFileDeps = new Set<string>();
54
+ let evaluate:
55
+ | Awaited<ReturnType<typeof import("./turbo-evaluator.js").createCompilationEvaluator>>
56
+ | undefined;
57
+ const fsReadFile = (filePath: string) => {
58
+ crossFileDeps.add(filePath);
59
+ return new Promise<string>((resolve, reject) =>
60
+ this.fs.readFile(filePath, "utf-8", (err, result) => {
61
+ if (err) return reject(err);
62
+ if (!result) return reject(new Error(`File not found: ${filePath}`));
63
+ resolve(result);
64
+ }),
65
+ );
66
+ };
67
+
68
+ try {
69
+ const result = await transform(code, this.resourcePath, this.rootContext, sourceMap);
70
+ debugLog("ts", result.code, this.resourcePath);
71
+
72
+ let css = extractCss(result.code);
73
+ debugLog("css", css, this.resourcePath);
74
+
75
+ const { resolved } = await resolveCrossFileConstant(
76
+ {
77
+ parse: (modulePath) => {
78
+ return parseModule(
79
+ {
80
+ extractExports: async (modulePath) => {
81
+ const sourceContents = await fsReadFile(modulePath);
82
+ return parseExports(sourceContents);
83
+ },
84
+ getTransformed: async (modulePath) => {
85
+ const sourceContent = await fsReadFile(modulePath);
86
+ return transform(sourceContent, modulePath, this.rootContext);
87
+ },
88
+ evaluateYakModule: async (modulePath: string) => {
89
+ crossFileDeps.add(modulePath);
90
+ /*
91
+ * Turbopack doesn't let us know when a compilation start so by using a singleton evaluator we
92
+ * we can at least ensture that we scan for file modifications only once per loader call
93
+ */
94
+ evaluate ??= await (
95
+ await import("./turbo-evaluator.js")
96
+ ).createCompilationEvaluator();
97
+ return evaluate(modulePath, (dep) => crossFileDeps.add(dep));
98
+ },
99
+ },
100
+ modulePath,
101
+ );
102
+ },
103
+ resolve: resolveFn,
104
+ },
105
+ this.resourcePath,
106
+ css,
107
+ );
108
+
109
+ // Register cross-file dependencies so turbopack re-runs this loader
110
+ // when any dependency changes (analogous to webpack's this.addDependency)
111
+ for (const dep of crossFileDeps) {
112
+ this.addDependency(dep);
113
+ }
114
+
115
+ const codeWithCrossFileResolved = replaceDataUrlImport(result.code, resolved);
116
+
117
+ debugLog("css-resolved", resolved, this.resourcePath);
118
+ // swc returns a JSON string, which babel-loader rejects as inputSourceMap
119
+ return callback(
120
+ null,
121
+ codeWithCrossFileResolved,
122
+ result.map ? JSON.parse(result.map) : undefined,
123
+ );
124
+ } catch (error) {
125
+ // Register cross-file dependencies even on error so turbopack re-runs
126
+ // this loader when a broken dependency is fixed.
127
+ for (const dep of crossFileDeps) {
128
+ this.addDependency(dep);
129
+ }
130
+ return callback(error instanceof Error ? error : new Error(String(error)));
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Swap the data-url import the SWC plugin emitted for one carrying the resolved css.
136
+ *
137
+ * Modules which import @yak/react without declaring styles get no such import and are
138
+ * returned unchanged. The import is matched from the start of the line so that source
139
+ * code merely mentioning a data url cannot be mistaken for it.
140
+ */
141
+ function replaceDataUrlImport(code: string, resolvedCss: string): string {
142
+ const lines = code.split("\n");
143
+ const lineIndex = lines.findIndex((line) => line.trimStart().startsWith(dataUrlImportPrefix));
144
+ if (lineIndex === -1) {
145
+ return code;
146
+ }
147
+ lines[lineIndex] = `${dataUrlImportPrefix}${Buffer.from(resolvedCss).toString("base64")}";`;
148
+ return lines.join("\n");
149
+ }
150
+
151
+ function createTransform(yakPluginOptions: any, yakSwcPluginPath: string) {
152
+ return (data: string, modulePath: string, rootPath: string, sourceMap?: any) =>
153
+ // https://github.com/vercel/next.js/blob/canary/packages/next/src/build/webpack/loaders/next-swc-loader.ts#L143
154
+ swcTransform(data, {
155
+ filename: modulePath,
156
+ inputSourceMap: sourceMap,
157
+ sourceMaps: true,
158
+ sourceFileName: modulePath,
159
+ sourceRoot: rootPath,
160
+ jsc: {
161
+ // Derive the parser from the file name the same way Next.js does
162
+ parser: getSwcParserOptions(modulePath),
163
+ experimental: {
164
+ // Keep `import data from "./data.json" with { type: "json" }` intact
165
+ keepImportAttributes: true,
166
+ plugins: [[yakSwcPluginPath, yakPluginOptions]],
167
+ },
168
+ transform: {
169
+ react: {
170
+ runtime: "preserve",
171
+ },
172
+ },
173
+ target: "es2022",
174
+ loose: false,
175
+ minify: {
176
+ compress: false,
177
+ mangle: false,
178
+ },
179
+ preserveAllComments: true,
180
+ },
181
+ minify: false,
182
+ isModule: true,
183
+ });
184
+ }
@@ -0,0 +1,28 @@
1
+ import { createViteYakPlugin } from "yak-internals/vite-plugin-core";
2
+
3
+ export type { ViteYakPluginOptions } from "yak-internals/vite-plugin-core";
4
+
5
+ /**
6
+ * Vite plugin for @yak/react: compiles the css``, styled`` and css prop
7
+ * styles in your source code to plain CSS at build time.
8
+ *
9
+ * Add it before the framework's plugin so it sees your original source code.
10
+ *
11
+ * @usage
12
+ * ```ts
13
+ * // vite.config.ts
14
+ * import { defineConfig } from "vite";
15
+ * import react from "@vitejs/plugin-react-swc";
16
+ * import { viteYak } from "@yak/react/vite";
17
+ *
18
+ * export default defineConfig({
19
+ * plugins: [viteYak(), react()],
20
+ * });
21
+ * ```
22
+ */
23
+ export const viteYak = createViteYakPlugin({
24
+ name: "@yak/react",
25
+ reactRefreshReg: true,
26
+ excludePattern: /packages\/yak-react/,
27
+ foldStatic: true,
28
+ });
@@ -0,0 +1,41 @@
1
+ import type { LoaderContext } from "webpack";
2
+ import type { YakConfigOptions } from "../withYak/index.js";
3
+ import { createDebugLogger } from "yak-internals/debug-logger";
4
+ import { extractCss } from "yak-internals/extract-css";
5
+ import { resolveCrossFileConstant } from "./lib/resolveCrossFileSelectors.js";
6
+
7
+ /**
8
+ * Transform typescript to css
9
+ *
10
+ * This loader takes the cached result from the yak tsloader
11
+ * and extracts the css from the generated comments
12
+ */
13
+ export default async function cssExtractLoader(
14
+ this: LoaderContext<YakConfigOptions>,
15
+ // Instead of the source code, we receive the extracted css
16
+ // from the yak-swc transformation
17
+ _code: string,
18
+ sourceMap: string | undefined,
19
+ ): Promise<string | void> {
20
+ const callback = this.async();
21
+ // Load the module from the original typescript request (without !=! and the query)
22
+ return this.loadModule(this.resourcePath, (err, source) => {
23
+ if (err) {
24
+ return callback(err);
25
+ }
26
+ if (!source) {
27
+ return callback(new Error(`Source code for ${this.resourcePath} is empty`));
28
+ }
29
+ const { debug } = this.getOptions();
30
+ const debugLog = createDebugLogger(debug, this._compiler?.context ?? process.cwd());
31
+
32
+ debugLog("ts", source, this.resourcePath);
33
+ const css = extractCss(source);
34
+ debugLog("css", css, this.resourcePath);
35
+
36
+ return resolveCrossFileConstant(this, this.context, css).then((result) => {
37
+ debugLog("css-resolved", result, this.resourcePath);
38
+ return callback(null, result, sourceMap);
39
+ }, callback);
40
+ });
41
+ }