@salty-css/core 0.0.1-alpha.303 → 0.0.1-alpha.304
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/bin/main.cjs +51 -50
- package/bin/main.js +2 -1
- package/compiler/as-class.cjs +592 -0
- package/compiler/as-class.d.ts +35 -0
- package/compiler/as-class.js +575 -0
- package/compiler/helpers.cjs +6 -0
- package/compiler/helpers.d.ts +3 -0
- package/compiler/helpers.js +7 -1
- package/compiler/index.cjs +8 -86
- package/compiler/index.js +1 -78
- package/package.json +5 -1
- package/salty-reset-StBt2yzJ.js +83 -0
- package/salty-reset-wJhVT2ys.cjs +83 -0
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
6
|
+
const esbuild = require("esbuild");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const saltyReset = require("../salty-reset-wJhVT2ys.cjs");
|
|
9
|
+
const promises = require("fs/promises");
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
const child_process = require("child_process");
|
|
12
|
+
const compiler_helpers = require("./helpers.cjs");
|
|
13
|
+
const defineTemplates = require("../define-templates-Deq1aCbN.cjs");
|
|
14
|
+
const dashCase = require("../dash-case-Cz8wwE9a.cjs");
|
|
15
|
+
const parseStyles = require("../parse-styles-D-p_guRO.cjs");
|
|
16
|
+
const css_merge = require("../css/merge.cjs");
|
|
17
|
+
const parsers_index = require("../parsers/index.cjs");
|
|
18
|
+
const console = require("console");
|
|
19
|
+
const compiler_getFunctionRange = require("./get-function-range.cjs");
|
|
20
|
+
function _interopNamespaceDefault(e) {
|
|
21
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
22
|
+
if (e) {
|
|
23
|
+
for (const k in e) {
|
|
24
|
+
if (k !== "default") {
|
|
25
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
26
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
get: () => e[k]
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
n.default = e;
|
|
34
|
+
return Object.freeze(n);
|
|
35
|
+
}
|
|
36
|
+
const esbuild__namespace = /* @__PURE__ */ _interopNamespaceDefault(esbuild);
|
|
37
|
+
class SaltyCompiler {
|
|
38
|
+
constructor(projectRootDir) {
|
|
39
|
+
__publicField(this, "importFile", (path2) => {
|
|
40
|
+
const now = Date.now();
|
|
41
|
+
return import(
|
|
42
|
+
/* @vite-ignore */
|
|
43
|
+
`${path2}?t=${now}`
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
__publicField(this, "cache", {
|
|
47
|
+
rcFile: null,
|
|
48
|
+
destDir: null,
|
|
49
|
+
externalModules: []
|
|
50
|
+
});
|
|
51
|
+
/**
|
|
52
|
+
* Locate and read the .saltyrc.json file starting from the current directory and moving up the directory tree.
|
|
53
|
+
* Caches the result to avoid redundant file reads.
|
|
54
|
+
*/
|
|
55
|
+
__publicField(this, "readRCFile", async (currentDir = this.projectRootDir) => {
|
|
56
|
+
if (this.cache.rcFile) return this.cache.rcFile;
|
|
57
|
+
if (currentDir === "/") throw new Error("Could not find .saltyrc.json file");
|
|
58
|
+
const rcPath = path.join(currentDir, ".saltyrc.json");
|
|
59
|
+
const rcContent = await promises.readFile(rcPath, "utf-8").then(JSON.parse).catch(() => void 0);
|
|
60
|
+
if (!rcContent) return this.readRCFile(path.join(currentDir, ".."));
|
|
61
|
+
this.cache.rcFile = rcContent;
|
|
62
|
+
return rcContent;
|
|
63
|
+
});
|
|
64
|
+
/**
|
|
65
|
+
* Get the project configuration from the .saltyrc.json file based on the current directory.
|
|
66
|
+
* If no specific project configuration is found, it falls back to the default project.
|
|
67
|
+
*/
|
|
68
|
+
__publicField(this, "getRCProjectConfig", async (dirname) => {
|
|
69
|
+
var _a, _b;
|
|
70
|
+
const rcFile = await this.readRCFile(dirname);
|
|
71
|
+
const projectConfig = (_a = rcFile.projects) == null ? void 0 : _a.find((project) => dirname.endsWith(project.dir || ""));
|
|
72
|
+
if (!projectConfig) return (_b = rcFile.projects) == null ? void 0 : _b.find((project) => project.dir === rcFile.defaultProject);
|
|
73
|
+
return projectConfig;
|
|
74
|
+
});
|
|
75
|
+
__publicField(this, "getExternalModules", (coreConfigPath) => {
|
|
76
|
+
if (this.cache.externalModules.length > 0) return this.cache.externalModules;
|
|
77
|
+
const content = fs.readFileSync(coreConfigPath, "utf8");
|
|
78
|
+
const match = content.match(/externalModules:\s?\[(.*)\]/);
|
|
79
|
+
if (!match) return [];
|
|
80
|
+
const externalModules = match[1].split(",").map((d) => d.replace(/['"`]/g, "").trim());
|
|
81
|
+
this.cache.externalModules = externalModules;
|
|
82
|
+
return externalModules;
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* Get the destination directory for generated files based on the project configuration.
|
|
86
|
+
* Caches the result to avoid redundant computations.
|
|
87
|
+
*/
|
|
88
|
+
__publicField(this, "getDestDir", async () => {
|
|
89
|
+
if (this.cache.destDir) return this.cache.destDir;
|
|
90
|
+
const projectConfig = await this.getRCProjectConfig(this.projectRootDir);
|
|
91
|
+
const destDir = path.join(this.projectRootDir, (projectConfig == null ? void 0 : projectConfig.saltygenDir) || "saltygen");
|
|
92
|
+
this.cache.destDir = destDir;
|
|
93
|
+
return destDir;
|
|
94
|
+
});
|
|
95
|
+
__publicField(this, "generateConfig", async () => {
|
|
96
|
+
const rcProject = await this.getRCProjectConfig(this.projectRootDir);
|
|
97
|
+
const destDir = await this.getDestDir();
|
|
98
|
+
const coreConfigPath = path.join(this.projectRootDir, (rcProject == null ? void 0 : rcProject.configDir) || "", "salty.config.ts");
|
|
99
|
+
const coreConfigDest = path.join(destDir, "salty.config.js");
|
|
100
|
+
const moduleType = await saltyReset.detectCurrentModuleType(this.projectRootDir);
|
|
101
|
+
const externalModules = this.getExternalModules(coreConfigPath);
|
|
102
|
+
await esbuild__namespace.build({
|
|
103
|
+
entryPoints: [coreConfigPath],
|
|
104
|
+
minify: true,
|
|
105
|
+
treeShaking: true,
|
|
106
|
+
bundle: true,
|
|
107
|
+
outfile: coreConfigDest,
|
|
108
|
+
format: moduleType,
|
|
109
|
+
external: externalModules
|
|
110
|
+
});
|
|
111
|
+
const { config } = await this.importFile(coreConfigDest);
|
|
112
|
+
return { config, path: coreConfigDest };
|
|
113
|
+
});
|
|
114
|
+
__publicField(this, "addConfigCache", (currentFile) => {
|
|
115
|
+
try {
|
|
116
|
+
const saltyCachedConfig = fs.readFileSync(path.join(this.projectRootDir, "saltygen/cache/config-cache.json"), "utf8");
|
|
117
|
+
if (!saltyCachedConfig) return `globalThis.saltyConfig = {};
|
|
118
|
+
|
|
119
|
+
${currentFile}`;
|
|
120
|
+
return `globalThis.saltyConfig = ${saltyCachedConfig};
|
|
121
|
+
|
|
122
|
+
${currentFile}`;
|
|
123
|
+
} catch {
|
|
124
|
+
return currentFile;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
__publicField(this, "getConfigCache", async () => {
|
|
128
|
+
const destDir = await this.getDestDir();
|
|
129
|
+
const coreConfigDest = path.join(destDir, "cache/config-cache.json");
|
|
130
|
+
const contents = fs.readFileSync(coreConfigDest, "utf8");
|
|
131
|
+
if (!contents) throw new Error("Could not find config cache file");
|
|
132
|
+
return JSON.parse(contents);
|
|
133
|
+
});
|
|
134
|
+
__publicField(this, "getConfig", async () => {
|
|
135
|
+
const cached = await this.getConfigCache();
|
|
136
|
+
const destDir = await this.getDestDir();
|
|
137
|
+
const coreConfigDest = path.join(destDir, "salty.config.js");
|
|
138
|
+
const { config } = await this.importFile(coreConfigDest);
|
|
139
|
+
return css_merge.mergeObjects(config, cached);
|
|
140
|
+
});
|
|
141
|
+
/**
|
|
142
|
+
* Generate CSS files based on the Salty CSS configuration and source files.
|
|
143
|
+
*/
|
|
144
|
+
__publicField(this, "generateCss", async (clean = true) => {
|
|
145
|
+
try {
|
|
146
|
+
const start = Date.now();
|
|
147
|
+
if (this.isProduction) saltyReset.logger.info("Generating CSS in production mode! 🔥");
|
|
148
|
+
else saltyReset.logger.info("Generating CSS in development mode! 🚀");
|
|
149
|
+
const globalCssFiles = [];
|
|
150
|
+
const cssFiles = [];
|
|
151
|
+
const destDir = await this.getDestDir();
|
|
152
|
+
const cssFile = path.join(destDir, "index.css");
|
|
153
|
+
const clearDistDir = () => {
|
|
154
|
+
if (fs.existsSync(destDir)) child_process.execSync("rm -rf " + destDir);
|
|
155
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
156
|
+
fs.mkdirSync(path.join(destDir, "css"));
|
|
157
|
+
fs.mkdirSync(path.join(destDir, "types"));
|
|
158
|
+
fs.mkdirSync(path.join(destDir, "js"));
|
|
159
|
+
fs.mkdirSync(path.join(destDir, "cache"));
|
|
160
|
+
};
|
|
161
|
+
if (clean) clearDistDir();
|
|
162
|
+
const files = /* @__PURE__ */ new Set();
|
|
163
|
+
const configFiles = /* @__PURE__ */ new Set();
|
|
164
|
+
async function collectFiles(src) {
|
|
165
|
+
const foldersToSkip = ["node_modules", "saltygen"];
|
|
166
|
+
const stats = fs.statSync(src);
|
|
167
|
+
if (stats.isDirectory()) {
|
|
168
|
+
const files2 = fs.readdirSync(src);
|
|
169
|
+
const shouldSkip = foldersToSkip.some((folder) => src.includes(folder));
|
|
170
|
+
if (shouldSkip) return;
|
|
171
|
+
await Promise.all(files2.map((file) => collectFiles(path.join(src, file))));
|
|
172
|
+
} else if (stats.isFile()) {
|
|
173
|
+
const validFile = compiler_helpers.isSaltyFile(src);
|
|
174
|
+
if (validFile) {
|
|
175
|
+
files.add(src);
|
|
176
|
+
const contents = fs.readFileSync(src, "utf8");
|
|
177
|
+
const hasDefineFunction = /define[\w\d]+\(/.test(contents);
|
|
178
|
+
if (hasDefineFunction) configFiles.add(src);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
await collectFiles(this.projectRootDir);
|
|
183
|
+
await this.generateConfigStyles(configFiles);
|
|
184
|
+
const generationResults = {
|
|
185
|
+
keyframes: [],
|
|
186
|
+
components: [],
|
|
187
|
+
classNames: []
|
|
188
|
+
};
|
|
189
|
+
await Promise.all(
|
|
190
|
+
[...files].map(async (src) => {
|
|
191
|
+
const { contents } = await this.compileSaltyFile(src, destDir);
|
|
192
|
+
for (const [name, value] of Object.entries(contents)) {
|
|
193
|
+
const resolved = await compiler_helpers.resolveExportValue(value, 1);
|
|
194
|
+
if (resolved.isKeyframes) {
|
|
195
|
+
generationResults.keyframes.push({
|
|
196
|
+
value: resolved,
|
|
197
|
+
src,
|
|
198
|
+
name
|
|
199
|
+
});
|
|
200
|
+
} else if (resolved.isClassName) {
|
|
201
|
+
generationResults.classNames.push({
|
|
202
|
+
...value,
|
|
203
|
+
src,
|
|
204
|
+
name
|
|
205
|
+
});
|
|
206
|
+
} else if (resolved.generator) {
|
|
207
|
+
generationResults.components.push({
|
|
208
|
+
...value,
|
|
209
|
+
src,
|
|
210
|
+
name
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
})
|
|
215
|
+
);
|
|
216
|
+
const config = await this.getConfig();
|
|
217
|
+
for (const keyframes of generationResults.keyframes) {
|
|
218
|
+
const { value } = keyframes;
|
|
219
|
+
const fileName = `a_${value.animationName}.css`;
|
|
220
|
+
const filePath = `css/${fileName}`;
|
|
221
|
+
const cssPath = path.join(destDir, filePath);
|
|
222
|
+
globalCssFiles.push(fileName);
|
|
223
|
+
fs.writeFileSync(cssPath, value.css);
|
|
224
|
+
}
|
|
225
|
+
const localCssFiles = {};
|
|
226
|
+
for (const componentResult of generationResults.components) {
|
|
227
|
+
const { src, name } = componentResult;
|
|
228
|
+
if (!localCssFiles[src]) localCssFiles[src] = [];
|
|
229
|
+
const generator = componentResult.generator._withBuildContext({
|
|
230
|
+
callerName: name,
|
|
231
|
+
isProduction: this.isProduction,
|
|
232
|
+
config
|
|
233
|
+
});
|
|
234
|
+
if (!cssFiles[generator.priority]) cssFiles[generator.priority] = [];
|
|
235
|
+
const styles = await generator.css;
|
|
236
|
+
if (!styles) continue;
|
|
237
|
+
cssFiles[generator.priority].push(generator.cssFileName);
|
|
238
|
+
const filePath = `css/${generator.cssFileName}`;
|
|
239
|
+
const cssPath = path.join(destDir, filePath);
|
|
240
|
+
fs.writeFileSync(cssPath, styles);
|
|
241
|
+
if (config.importStrategy === "component") {
|
|
242
|
+
localCssFiles[src].push(generator.cssFileName);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
for (const classNameResult of generationResults.classNames) {
|
|
246
|
+
const { src, name } = classNameResult;
|
|
247
|
+
if (!localCssFiles[src]) localCssFiles[src] = [];
|
|
248
|
+
const generator = classNameResult.generator._withBuildContext({
|
|
249
|
+
callerName: name,
|
|
250
|
+
isProduction: this.isProduction,
|
|
251
|
+
config
|
|
252
|
+
});
|
|
253
|
+
const styles = await generator.css;
|
|
254
|
+
if (!styles) continue;
|
|
255
|
+
if (!cssFiles[generator.priority]) cssFiles[generator.priority] = [];
|
|
256
|
+
cssFiles[generator.priority].push(generator.cssFileName);
|
|
257
|
+
const filePath = `css/${generator.cssFileName}`;
|
|
258
|
+
const cssPath = path.join(destDir, filePath);
|
|
259
|
+
fs.writeFileSync(cssPath, styles);
|
|
260
|
+
if (config.importStrategy === "component") {
|
|
261
|
+
localCssFiles[src].push(generator.cssFileName);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (config.importStrategy === "component") {
|
|
265
|
+
Object.entries(localCssFiles).forEach(([src, localCssFile]) => {
|
|
266
|
+
const cssContent2 = localCssFile.map((file) => `@import url('./${file}');`).join("\n");
|
|
267
|
+
const hashName = dashCase.toHash(src, 6);
|
|
268
|
+
const parsedPath = path.parse(src);
|
|
269
|
+
const dasherized = dashCase.dashCase(parsedPath.name);
|
|
270
|
+
const cssFile2 = path.join(destDir, `css/f_${dasherized}-${hashName}.css`);
|
|
271
|
+
fs.writeFileSync(cssFile2, cssContent2 || `/* Empty file */`);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
const otherGlobalCssFiles = globalCssFiles.map((file) => `@import url('./css/${file}');`).join("\n");
|
|
275
|
+
const globalCssFilenames = ["_variables.css", "_reset.css", "_global.css", "_templates.css"];
|
|
276
|
+
const importsWithData = globalCssFilenames.filter((file) => {
|
|
277
|
+
try {
|
|
278
|
+
const data = fs.readFileSync(path.join(destDir, "css", file), "utf8");
|
|
279
|
+
return data.length > 0;
|
|
280
|
+
} catch {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
const globalImports = importsWithData.map((file) => `@import url('./css/${file}');`);
|
|
285
|
+
const generatorText = "/*!\n * Generated with Salty CSS (https://salty-css.dev)\n * Do not edit this file directly\n */\n";
|
|
286
|
+
let cssContent = `${generatorText}@layer reset, global, templates, l0, l1, l2, l3, l4, l5, l6, l7, l8;
|
|
287
|
+
|
|
288
|
+
${globalImports.join(
|
|
289
|
+
"\n"
|
|
290
|
+
)}
|
|
291
|
+
${otherGlobalCssFiles}`;
|
|
292
|
+
if (config.importStrategy !== "component") {
|
|
293
|
+
const mergedContent = cssFiles.reduce((acc, val, layer) => {
|
|
294
|
+
const layerContent = val.reduce((layerAcc, file) => {
|
|
295
|
+
var _a;
|
|
296
|
+
const filepath = path.join(destDir, "css", file);
|
|
297
|
+
const css = fs.readFileSync(filepath, "utf8");
|
|
298
|
+
const filepathHash = ((_a = /.*-([^-]+)-\d+.css/.exec(file)) == null ? void 0 : _a.at(1)) || dashCase.toHash(filepath, 6);
|
|
299
|
+
if (layerAcc.includes(filepathHash)) return layerAcc;
|
|
300
|
+
return `${layerAcc}
|
|
301
|
+
/*start:${filepathHash}-${file}*/
|
|
302
|
+
${css}
|
|
303
|
+
/*end:${filepathHash}*/
|
|
304
|
+
`;
|
|
305
|
+
}, "");
|
|
306
|
+
const layerFileName = `l_${layer}.css`;
|
|
307
|
+
const layerFilePath = path.join(destDir, "css", layerFileName);
|
|
308
|
+
const layerContentWithLayer = `@layer l${layer} { ${layerContent}
|
|
309
|
+
}`;
|
|
310
|
+
fs.writeFileSync(layerFilePath, layerContentWithLayer);
|
|
311
|
+
return `${acc}
|
|
312
|
+
@import url('./css/${layerFileName}');`;
|
|
313
|
+
}, "");
|
|
314
|
+
cssContent += mergedContent;
|
|
315
|
+
}
|
|
316
|
+
fs.writeFileSync(cssFile, cssContent);
|
|
317
|
+
const end = Date.now();
|
|
318
|
+
const time = end - start;
|
|
319
|
+
const emoji = time < 200 ? "🔥" : time < 500 ? "🚀" : time < 1e3 ? "🎉" : time < 2e3 ? "🚗" : time < 5e3 ? "🤔" : "🥴";
|
|
320
|
+
saltyReset.logger.info(`Generated CSS in ${time}ms! ${emoji}`);
|
|
321
|
+
} catch (e) {
|
|
322
|
+
console.error(e);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
__publicField(this, "generateConfigStyles", async (configFiles) => {
|
|
326
|
+
var _a, _b;
|
|
327
|
+
const destDir = await this.getDestDir();
|
|
328
|
+
const generationResults = {
|
|
329
|
+
mediaQueries: [],
|
|
330
|
+
globalStyles: [],
|
|
331
|
+
variables: [],
|
|
332
|
+
templates: []
|
|
333
|
+
};
|
|
334
|
+
await Promise.all(
|
|
335
|
+
[...configFiles].map(async (src) => {
|
|
336
|
+
const { contents, outputFilePath } = await this.compileSaltyFile(src, destDir);
|
|
337
|
+
Object.entries(contents).forEach(([name, value]) => {
|
|
338
|
+
if (value.isMedia) generationResults.mediaQueries.push([name, value]);
|
|
339
|
+
else if (value.isGlobalDefine) generationResults.globalStyles.push(value);
|
|
340
|
+
else if (value.isDefineVariables) generationResults.variables.push(value);
|
|
341
|
+
else if (value.isDefineTemplates) generationResults.templates.push(value._setPath(`${name};;${outputFilePath}`));
|
|
342
|
+
});
|
|
343
|
+
})
|
|
344
|
+
);
|
|
345
|
+
const { config, path: configPath } = await this.generateConfig();
|
|
346
|
+
const configCacheContent = { ...config };
|
|
347
|
+
const { mediaQueries } = generationResults;
|
|
348
|
+
configCacheContent.mediaQueries = Object.fromEntries(mediaQueries.map(([name, value]) => [`@${name}`, value]));
|
|
349
|
+
const mediaQueryKeys = mediaQueries.map(([name]) => `'@${name}'`).join(" | ");
|
|
350
|
+
const variableTokens = /* @__PURE__ */ new Set();
|
|
351
|
+
const parseVariables = async (obj, path2 = []) => {
|
|
352
|
+
if (!obj) return [];
|
|
353
|
+
const promises2 = Object.entries(obj).map(async ([key, value]) => {
|
|
354
|
+
const parseVariable = async (value2) => {
|
|
355
|
+
if (!value2) return void 0;
|
|
356
|
+
if (value2 instanceof Promise) return await parseVariable(await value2);
|
|
357
|
+
if (typeof value2 === "function") return await parseVariable(await value2());
|
|
358
|
+
if (typeof value2 === "object") return await parseVariables(value2, [...path2, key]);
|
|
359
|
+
const dottedKey = saltyReset.dotCase(key);
|
|
360
|
+
const dashedKey = dashCase.dashCase(key);
|
|
361
|
+
const tsName = [...path2, dottedKey].join(".");
|
|
362
|
+
variableTokens.add(`"${tsName}"`);
|
|
363
|
+
const cssName = [...path2.map(dashCase.dashCase), dashedKey].join("-");
|
|
364
|
+
const result = parseStyles.parseVariableTokens(value2);
|
|
365
|
+
if (!result) return `--${cssName}: ${value2};`;
|
|
366
|
+
return `--${cssName}: ${result.transformed};`;
|
|
367
|
+
};
|
|
368
|
+
return await parseVariable(value);
|
|
369
|
+
});
|
|
370
|
+
const results = await Promise.all(promises2);
|
|
371
|
+
return results.flat();
|
|
372
|
+
};
|
|
373
|
+
const parseResponsiveVariables = async (obj) => {
|
|
374
|
+
if (!obj) return [];
|
|
375
|
+
const promises2 = Object.entries(obj).map(async ([mediaQuery, values]) => {
|
|
376
|
+
const variables = await parseVariables(values);
|
|
377
|
+
if (mediaQuery === "base") return variables.join("");
|
|
378
|
+
if (configCacheContent.mediaQueries[mediaQuery]) {
|
|
379
|
+
const mediaQueryValue = configCacheContent.mediaQueries[mediaQuery];
|
|
380
|
+
return `${mediaQueryValue} { ${variables.join("")} }`;
|
|
381
|
+
}
|
|
382
|
+
return `${mediaQuery} { ${variables.join("")} }`;
|
|
383
|
+
});
|
|
384
|
+
const results = await Promise.all(promises2);
|
|
385
|
+
return results.flat();
|
|
386
|
+
};
|
|
387
|
+
const parseConditionalVariables = async (obj) => {
|
|
388
|
+
if (!obj) return [];
|
|
389
|
+
const promises2 = Object.entries(obj).map(async ([property, conditions]) => {
|
|
390
|
+
const promises22 = Object.entries(conditions).map(async ([condition, values]) => {
|
|
391
|
+
const variables = await parseVariables(values, [property]);
|
|
392
|
+
const conditionScope = `.${property}-${condition}, [data-${property}="${condition}"]`;
|
|
393
|
+
const combined = variables.join("");
|
|
394
|
+
return `${conditionScope} { ${combined} }`;
|
|
395
|
+
});
|
|
396
|
+
const result = await Promise.all(promises22);
|
|
397
|
+
return result.flat();
|
|
398
|
+
});
|
|
399
|
+
const results = await Promise.all(promises2);
|
|
400
|
+
return results.flat();
|
|
401
|
+
};
|
|
402
|
+
const getStaticVariables = (variables = {}) => {
|
|
403
|
+
return { ...variables, responsive: void 0, conditional: void 0 };
|
|
404
|
+
};
|
|
405
|
+
const getGeneratedVariables = (type) => {
|
|
406
|
+
return generationResults.variables.map((factory) => {
|
|
407
|
+
if (type === "static") return getStaticVariables(factory._current);
|
|
408
|
+
return factory._current[type];
|
|
409
|
+
});
|
|
410
|
+
};
|
|
411
|
+
const _staticVariables = css_merge.mergeObjects(getStaticVariables(config.variables), getGeneratedVariables("static"));
|
|
412
|
+
const staticVariables = await parseVariables(_staticVariables);
|
|
413
|
+
const _responsiveVariables = css_merge.mergeObjects((_a = config.variables) == null ? void 0 : _a.responsive, getGeneratedVariables("responsive"));
|
|
414
|
+
const responsiveVariables = await parseResponsiveVariables(_responsiveVariables);
|
|
415
|
+
const _conditionalVariables = css_merge.mergeObjects((_b = config.variables) == null ? void 0 : _b.conditional, getGeneratedVariables("conditional"));
|
|
416
|
+
const conditionalVariables = await parseConditionalVariables(_conditionalVariables);
|
|
417
|
+
const variablesPath = path.join(destDir, "css/_variables.css");
|
|
418
|
+
const variablesCss = `:root { ${staticVariables.join("")} ${responsiveVariables.join("")} } ${conditionalVariables.join("")}`;
|
|
419
|
+
fs.writeFileSync(variablesPath, variablesCss);
|
|
420
|
+
configCacheContent.staticVariables = _staticVariables;
|
|
421
|
+
const globalStylesPath = path.join(destDir, "css/_global.css");
|
|
422
|
+
const mergedGlobalStyles = css_merge.mergeObjects(config.global, generationResults.globalStyles);
|
|
423
|
+
const globalStylesString = await parseStyles.parseAndJoinStyles(mergedGlobalStyles, "");
|
|
424
|
+
fs.writeFileSync(globalStylesPath, `@layer global { ${globalStylesString} }`);
|
|
425
|
+
const resetStylesPath = path.join(destDir, "css/_reset.css");
|
|
426
|
+
const getResetStyles = () => {
|
|
427
|
+
if (config.reset === "none") return {};
|
|
428
|
+
if (typeof config.reset === "object") return config.reset;
|
|
429
|
+
return saltyReset.saltyReset;
|
|
430
|
+
};
|
|
431
|
+
const resetStyles = getResetStyles();
|
|
432
|
+
const resetStylesString = await parseStyles.parseAndJoinStyles(resetStyles, "");
|
|
433
|
+
fs.writeFileSync(resetStylesPath, `@layer reset { ${resetStylesString} }`);
|
|
434
|
+
const templateStylesPath = path.join(destDir, "css/_templates.css");
|
|
435
|
+
const templates = css_merge.mergeObjects(config.templates, generationResults.templates);
|
|
436
|
+
const templateStylesString = await parsers_index.parseTemplates(templates);
|
|
437
|
+
const templateTokens = parsers_index.getTemplateTypes(templates);
|
|
438
|
+
fs.writeFileSync(templateStylesPath, `@layer templates { ${templateStylesString} }`);
|
|
439
|
+
configCacheContent.templates = templates;
|
|
440
|
+
const configTemplateFactories = config.templates ? [defineTemplates.defineTemplates(config.templates)._setPath(`config;;${configPath}`)] : [];
|
|
441
|
+
const templateFactories = css_merge.mergeFactories(generationResults.templates, configTemplateFactories);
|
|
442
|
+
configCacheContent.templatePaths = Object.fromEntries(Object.entries(templateFactories).map(([key, faktory]) => [key, faktory._path]));
|
|
443
|
+
const tsTokensPath = path.join(destDir, "types/css-tokens.d.ts");
|
|
444
|
+
const tsVariableTokens = [...variableTokens].join("|");
|
|
445
|
+
const tsTokensTypes = `
|
|
446
|
+
// Variable types
|
|
447
|
+
type VariableTokens = ${tsVariableTokens || `''`};
|
|
448
|
+
type PropertyValueToken = \`{\${VariableTokens}}\`;
|
|
449
|
+
|
|
450
|
+
// Template types
|
|
451
|
+
type TemplateTokens = {
|
|
452
|
+
${Object.entries(templateTokens).map(([key, value]) => `${key}?: ${value}`).join("\n")}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Media query types
|
|
456
|
+
type MediaQueryKeys = ${mediaQueryKeys || `''`};
|
|
457
|
+
`;
|
|
458
|
+
fs.writeFileSync(tsTokensPath, tsTokensTypes);
|
|
459
|
+
const configCachePath = path.join(destDir, "cache/config-cache.json");
|
|
460
|
+
fs.writeFileSync(configCachePath, JSON.stringify(configCacheContent, null, 2));
|
|
461
|
+
const corePackageRoot = compiler_helpers.getCorePackageRoot();
|
|
462
|
+
const configCacheSecondaryPath = path.join(corePackageRoot, "cache/config-cache.json");
|
|
463
|
+
fs.writeFileSync(configCacheSecondaryPath, JSON.stringify(configCacheContent, null, 2));
|
|
464
|
+
});
|
|
465
|
+
__publicField(this, "compileSaltyFile", async (sourceFilePath, outputDirectory) => {
|
|
466
|
+
const hashedName = dashCase.toHash(sourceFilePath);
|
|
467
|
+
const tempDir = path.join(outputDirectory, "./temp");
|
|
468
|
+
if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir);
|
|
469
|
+
const parsed = path.parse(sourceFilePath);
|
|
470
|
+
let currentFile = fs.readFileSync(sourceFilePath, "utf8");
|
|
471
|
+
currentFile = this.replaceStyledTag(currentFile);
|
|
472
|
+
currentFile = this.addConfigCache(currentFile);
|
|
473
|
+
const outputFilePath = path.join(outputDirectory, "js", hashedName + ".js");
|
|
474
|
+
const rcProject = await this.getRCProjectConfig(this.projectRootDir);
|
|
475
|
+
const coreConfigPath = path.join(this.projectRootDir, (rcProject == null ? void 0 : rcProject.configDir) || "", "salty.config.ts");
|
|
476
|
+
const externalModules = this.getExternalModules(coreConfigPath);
|
|
477
|
+
const moduleType = await saltyReset.detectCurrentModuleType(this.projectRootDir);
|
|
478
|
+
await esbuild__namespace.build({
|
|
479
|
+
stdin: {
|
|
480
|
+
contents: currentFile,
|
|
481
|
+
sourcefile: parsed.base,
|
|
482
|
+
resolveDir: parsed.dir,
|
|
483
|
+
loader: "tsx"
|
|
484
|
+
},
|
|
485
|
+
minify: false,
|
|
486
|
+
treeShaking: true,
|
|
487
|
+
bundle: true,
|
|
488
|
+
outfile: outputFilePath,
|
|
489
|
+
format: moduleType,
|
|
490
|
+
target: ["node20"],
|
|
491
|
+
keepNames: true,
|
|
492
|
+
external: externalModules,
|
|
493
|
+
packages: "external",
|
|
494
|
+
plugins: [
|
|
495
|
+
{
|
|
496
|
+
name: "test",
|
|
497
|
+
setup: (build) => {
|
|
498
|
+
build.onLoad({ filter: /.*\.css|salty|styles|styled\.ts/ }, (args) => {
|
|
499
|
+
const original = fs.readFileSync(args.path, "utf8");
|
|
500
|
+
const modified = this.replaceStyledTag(original);
|
|
501
|
+
return { contents: modified, loader: "ts" };
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
]
|
|
506
|
+
});
|
|
507
|
+
const contents = await this.importFile(outputFilePath);
|
|
508
|
+
return { contents, outputFilePath };
|
|
509
|
+
});
|
|
510
|
+
__publicField(this, "minimizeFile", async (file) => {
|
|
511
|
+
var _a, _b;
|
|
512
|
+
try {
|
|
513
|
+
const destDir = await this.getDestDir();
|
|
514
|
+
const validFile = compiler_helpers.isSaltyFile(file);
|
|
515
|
+
if (validFile) {
|
|
516
|
+
const original = fs.readFileSync(file, "utf8");
|
|
517
|
+
const config = await this.getConfig();
|
|
518
|
+
const { contents } = await this.compileSaltyFile(file, destDir);
|
|
519
|
+
let current = original;
|
|
520
|
+
for (const [name, value] of Object.entries(contents)) {
|
|
521
|
+
const resolved = await compiler_helpers.resolveExportValue(value, 1);
|
|
522
|
+
if (resolved.isKeyframes) continue;
|
|
523
|
+
if (!resolved.generator) continue;
|
|
524
|
+
const generator = resolved.generator._withBuildContext({
|
|
525
|
+
callerName: name,
|
|
526
|
+
isProduction: this.isProduction,
|
|
527
|
+
config
|
|
528
|
+
});
|
|
529
|
+
const [start, end] = await compiler_getFunctionRange.getFunctionRange(current, name);
|
|
530
|
+
const range = current.slice(start, end);
|
|
531
|
+
if (resolved.isClassName) {
|
|
532
|
+
const copy = current;
|
|
533
|
+
const clientVersion = ` ${name} = className("${generator.classNames}")`;
|
|
534
|
+
current = current.replace(range, clientVersion);
|
|
535
|
+
if (copy === current) console.error("Minimize file failed to change content", { name });
|
|
536
|
+
}
|
|
537
|
+
if (range.includes("styled")) {
|
|
538
|
+
const tagName = (_b = (_a = /styled\(([^,]+),/.exec(range)) == null ? void 0 : _a.at(1)) == null ? void 0 : _b.trim();
|
|
539
|
+
const copy = current;
|
|
540
|
+
const clientVersion = ` ${name} = styled(${tagName}, "${generator.classNames}", ${JSON.stringify(generator.clientProps)})`;
|
|
541
|
+
current = current.replace(range, clientVersion);
|
|
542
|
+
if (copy === current) console.error("Minimize file failed to change content", { name, tagName });
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
if (config.importStrategy === "component") {
|
|
546
|
+
const fileHash = dashCase.toHash(file, 6);
|
|
547
|
+
const parsed = path.parse(file);
|
|
548
|
+
const dasherized = dashCase.dashCase(parsed.name);
|
|
549
|
+
const cssFileName = `f_${dasherized}-${fileHash}.css`;
|
|
550
|
+
current = `import '../../saltygen/css/${cssFileName}';
|
|
551
|
+
${current}`;
|
|
552
|
+
}
|
|
553
|
+
current = current.replace(`@salty-css/react/class-name`, `@salty-css/react/class-name-client`);
|
|
554
|
+
current = current.replace(`{ styled }`, `{ styledClient as styled }`);
|
|
555
|
+
current = current.replace(`@salty-css/react/styled`, `@salty-css/react/styled-client`);
|
|
556
|
+
return current;
|
|
557
|
+
}
|
|
558
|
+
} catch (e) {
|
|
559
|
+
console.error("Error in minimizeFile:", e);
|
|
560
|
+
}
|
|
561
|
+
return void 0;
|
|
562
|
+
});
|
|
563
|
+
__publicField(this, "replaceStyledTag", (currentFile) => {
|
|
564
|
+
return currentFile.replace(/styled\(([^"'`{,]+),/g, (match, tag) => {
|
|
565
|
+
const isString = /^['"`]/.test(tag);
|
|
566
|
+
if (isString) return match;
|
|
567
|
+
const isImportedRegExp = new RegExp(`import[^;]*${tag}[,\\s{][^;]*from\\s?([^{};]+);`);
|
|
568
|
+
const isImported = isImportedRegExp.test(currentFile);
|
|
569
|
+
if (!isImported) return match;
|
|
570
|
+
const importResult = isImportedRegExp.exec(currentFile);
|
|
571
|
+
if (importResult) {
|
|
572
|
+
const importPath = importResult.at(1);
|
|
573
|
+
const isSaltyImport = compiler_helpers.saltyFileExtensions.some((ext) => importPath == null ? void 0 : importPath.includes(ext));
|
|
574
|
+
if (isSaltyImport) return match;
|
|
575
|
+
}
|
|
576
|
+
return "styled('div',";
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
this.projectRootDir = projectRootDir;
|
|
580
|
+
if (typeof process === "undefined") {
|
|
581
|
+
throw new Error("SaltyServer can only be used in a Node.js environment.");
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
get isProduction() {
|
|
585
|
+
try {
|
|
586
|
+
return process.env["NODE_ENV"] !== "development";
|
|
587
|
+
} catch {
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
exports.SaltyCompiler = SaltyCompiler;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare class SaltyCompiler {
|
|
2
|
+
projectRootDir: string;
|
|
3
|
+
importFile: (path: string) => Promise<any>;
|
|
4
|
+
private cache;
|
|
5
|
+
constructor(projectRootDir: string);
|
|
6
|
+
private get isProduction();
|
|
7
|
+
/**
|
|
8
|
+
* Locate and read the .saltyrc.json file starting from the current directory and moving up the directory tree.
|
|
9
|
+
* Caches the result to avoid redundant file reads.
|
|
10
|
+
*/
|
|
11
|
+
private readRCFile;
|
|
12
|
+
/**
|
|
13
|
+
* Get the project configuration from the .saltyrc.json file based on the current directory.
|
|
14
|
+
* If no specific project configuration is found, it falls back to the default project.
|
|
15
|
+
*/
|
|
16
|
+
private getRCProjectConfig;
|
|
17
|
+
private getExternalModules;
|
|
18
|
+
/**
|
|
19
|
+
* Get the destination directory for generated files based on the project configuration.
|
|
20
|
+
* Caches the result to avoid redundant computations.
|
|
21
|
+
*/
|
|
22
|
+
private getDestDir;
|
|
23
|
+
private generateConfig;
|
|
24
|
+
private addConfigCache;
|
|
25
|
+
private getConfigCache;
|
|
26
|
+
private getConfig;
|
|
27
|
+
/**
|
|
28
|
+
* Generate CSS files based on the Salty CSS configuration and source files.
|
|
29
|
+
*/
|
|
30
|
+
generateCss: (clean?: boolean) => Promise<void>;
|
|
31
|
+
private generateConfigStyles;
|
|
32
|
+
private compileSaltyFile;
|
|
33
|
+
minimizeFile: (file: string) => Promise<string | undefined>;
|
|
34
|
+
private replaceStyledTag;
|
|
35
|
+
}
|