@gustcss/vite 0.5.1 → 0.5.3
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/dist/index.cjs +181 -76
- package/dist/index.mjs +191 -61
- package/package.json +6 -4
package/dist/index.cjs
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
Object.
|
|
2
|
-
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
//#region \0rolldown/runtime.js
|
|
3
6
|
var __create = Object.create;
|
|
4
7
|
var __defProp = Object.defineProperty;
|
|
5
8
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
9
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
10
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
11
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
12
|
+
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
9
13
|
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
}
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
15
|
+
key = keys[i];
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
17
|
+
get: ((k) => from[k]).bind(null, key),
|
|
18
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
19
|
+
});
|
|
20
20
|
}
|
|
21
21
|
return to;
|
|
22
22
|
};
|
|
@@ -24,15 +24,124 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
value: mod,
|
|
25
25
|
enumerable: true
|
|
26
26
|
}) : target, mod));
|
|
27
|
-
|
|
28
27
|
//#endregion
|
|
29
28
|
let child_process = require("child_process");
|
|
30
29
|
let path = require("path");
|
|
31
|
-
path = __toESM(path);
|
|
30
|
+
path = __toESM(path, 1);
|
|
32
31
|
let fs = require("fs");
|
|
33
|
-
fs = __toESM(fs);
|
|
34
|
-
|
|
32
|
+
fs = __toESM(fs, 1);
|
|
33
|
+
let crypto = require("crypto");
|
|
34
|
+
//#endregion
|
|
35
35
|
//#region src/index.js
|
|
36
|
+
var import_runner = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
37
|
+
/**
|
|
38
|
+
* gustcss shared runner — helpers for the @gustcss/postcss and @gustcss/vite
|
|
39
|
+
* plugins.
|
|
40
|
+
*
|
|
41
|
+
* These functions centralize the four pieces of logic that the two plugins
|
|
42
|
+
* previously duplicated:
|
|
43
|
+
* - binary discovery (resolveBinary)
|
|
44
|
+
* - config-file detection (resolveConfig)
|
|
45
|
+
* - temporary config-file lifecycle (withTempConfig)
|
|
46
|
+
* - CLI argument assembly (buildArgs)
|
|
47
|
+
*
|
|
48
|
+
* They are pure (no module-level state) and accept `fs` via dependency
|
|
49
|
+
* injection so callers and tests can substitute it. The implementation uses
|
|
50
|
+
* only Node's built-in `path`, `fs`, and `crypto` — no third-party dependencies.
|
|
51
|
+
*
|
|
52
|
+
* This module is NOT a public API. It is inlined into each plugin's `dist`
|
|
53
|
+
* bundle at build time (rolldown) and is never published as a standalone
|
|
54
|
+
* source file. The `npm/shared/` directory deliberately has no own
|
|
55
|
+
* package.json with a public name, only a private one for running tests.
|
|
56
|
+
*
|
|
57
|
+
* @security Paths (cwd, config, output) are trusted: they originate from the
|
|
58
|
+
* developer's build configuration. These helpers are for build-time use only.
|
|
59
|
+
*/
|
|
60
|
+
const path$2 = require("path");
|
|
61
|
+
const crypto$1 = require("crypto");
|
|
62
|
+
const DEFAULT_CONFIG_NAMES = ["gustcss.config.json", "gustcss.config.js"];
|
|
63
|
+
/**
|
|
64
|
+
* Locate the gustcss CLI binary, trying three paths in order:
|
|
65
|
+
* 1. <cwd>/node_modules/.bin/gustcss (normal local install)
|
|
66
|
+
* 2. <cwd>/../../bin/gustcss (sample projects in this repo)
|
|
67
|
+
* 3. 'gustcss' (global install — always accepted)
|
|
68
|
+
*
|
|
69
|
+
* @param {{ cwd: string, fs?: { existsSync: Function } }} options
|
|
70
|
+
* @returns {string} the resolved binary path or the bare 'gustcss' sentinel
|
|
71
|
+
*/
|
|
72
|
+
function resolveBinary({ cwd, fs: fs$2 = require("fs") }) {
|
|
73
|
+
const candidates = [path$2.join(cwd, "node_modules", ".bin", "gustcss"), path$2.join(cwd, "..", "..", "bin", "gustcss")];
|
|
74
|
+
for (const candidate of candidates) if (fs$2.existsSync(candidate)) return candidate;
|
|
75
|
+
return "gustcss";
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Determine which config file the CLI should use.
|
|
79
|
+
* - If an explicit configPath is given, it is returned as-is (no fs access).
|
|
80
|
+
* - Otherwise auto-detect gustcss.config.json, then gustcss.config.js, in cwd.
|
|
81
|
+
* - Returns null when nothing is found (caller falls back to a temp config).
|
|
82
|
+
*
|
|
83
|
+
* @param {{ cwd: string, configPath?: string, fs?: { existsSync: Function } }} options
|
|
84
|
+
* @returns {string|null}
|
|
85
|
+
*/
|
|
86
|
+
function resolveConfig({ cwd, configPath, fs: fs$3 = require("fs") }) {
|
|
87
|
+
if (configPath) return configPath;
|
|
88
|
+
for (const name of DEFAULT_CONFIG_NAMES) {
|
|
89
|
+
const fullPath = path$2.join(cwd, name);
|
|
90
|
+
if (fs$3.existsSync(fullPath)) return fullPath;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Write a temporary config file ({ content }) at <cwd>/<prefix>.<random8hex>.tmp.json,
|
|
96
|
+
* invoke `fn` with the temp file path, and always clean the file up afterwards.
|
|
97
|
+
*
|
|
98
|
+
* The file is created with the 'wx' flag (O_EXCL equivalent): if a file with
|
|
99
|
+
* the same name already exists the write fails, preventing symlink-redirection
|
|
100
|
+
* attacks and parallel-run collisions. Because the name contains 8 random bytes
|
|
101
|
+
* the probability of a collision is negligible.
|
|
102
|
+
*
|
|
103
|
+
* @param {{ cwd: string, content: any, prefix: string, fs?: { writeFileSync: Function, existsSync: Function, unlinkSync: Function } }} options
|
|
104
|
+
* @param {(tempConfigPath: string) => any} fn
|
|
105
|
+
* @returns {any} whatever `fn` returns
|
|
106
|
+
*/
|
|
107
|
+
function withTempConfig({ cwd, content, prefix, fs: fs$4 = require("fs") }, fn) {
|
|
108
|
+
const tmpName = `${prefix}.${crypto$1.randomBytes(8).toString("hex")}.tmp.json`;
|
|
109
|
+
const tempConfigPath = path$2.join(cwd, tmpName);
|
|
110
|
+
fs$4.writeFileSync(tempConfigPath, JSON.stringify({ content }), { flag: "wx" });
|
|
111
|
+
try {
|
|
112
|
+
return fn(tempConfigPath);
|
|
113
|
+
} finally {
|
|
114
|
+
if (fs$4.existsSync(tempConfigPath)) fs$4.unlinkSync(tempConfigPath);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Assemble the CLI argument array.
|
|
119
|
+
*
|
|
120
|
+
* @param {object} options
|
|
121
|
+
* @param {'stdout'|'output'} options.mode 'stdout' emits to stdout (postcss);
|
|
122
|
+
* 'output' writes to a file (-o, vite).
|
|
123
|
+
* @param {string} [options.output] output path (required for 'output' mode)
|
|
124
|
+
* @param {boolean} [options.cssLayers] append --css-layers when truthy
|
|
125
|
+
* @param {boolean} [options.watch] append --watch when truthy
|
|
126
|
+
* @param {string|null} [options.configPath] append --config <path> when present
|
|
127
|
+
* @returns {string[]}
|
|
128
|
+
*/
|
|
129
|
+
function buildArgs({ mode, output, cssLayers, configPath, watch }) {
|
|
130
|
+
const args = ["build"];
|
|
131
|
+
if (watch) args.push("--watch");
|
|
132
|
+
if (mode === "stdout") args.push("--stdout");
|
|
133
|
+
else args.push("-o", output);
|
|
134
|
+
if (cssLayers) args.push("--css-layers");
|
|
135
|
+
if (configPath) args.push("--config", configPath);
|
|
136
|
+
return args;
|
|
137
|
+
}
|
|
138
|
+
module.exports = {
|
|
139
|
+
resolveBinary,
|
|
140
|
+
resolveConfig,
|
|
141
|
+
withTempConfig,
|
|
142
|
+
buildArgs
|
|
143
|
+
};
|
|
144
|
+
})))(), 1);
|
|
36
145
|
/**
|
|
37
146
|
* @gustcss/vite - Vite plugin for CSS Utility Generator
|
|
38
147
|
*
|
|
@@ -41,6 +150,10 @@ fs = __toESM(fs);
|
|
|
41
150
|
* export default defineConfig({
|
|
42
151
|
* plugins: [gustcss({ output: 'src/styles/utility.css' })],
|
|
43
152
|
* })
|
|
153
|
+
*
|
|
154
|
+
* @security Configuration paths (content, config, output) are trusted as they
|
|
155
|
+
* are provided by the developer in their build configuration. This plugin is
|
|
156
|
+
* designed for build-time use only and should not process untrusted input.
|
|
44
157
|
*/
|
|
45
158
|
function cssUtility(opts = {}) {
|
|
46
159
|
const output = opts.output || "src/styles/utility.css";
|
|
@@ -49,55 +162,50 @@ function cssUtility(opts = {}) {
|
|
|
49
162
|
const outputToCssLayers = opts.outputToCssLayers;
|
|
50
163
|
let watchProcess = null;
|
|
51
164
|
function findBinary(cwd) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
];
|
|
57
|
-
for (const p of possiblePaths) if (p === "gustcss" || fs.default.existsSync(p)) return p;
|
|
58
|
-
throw new Error("gustcss binary not found");
|
|
165
|
+
return import_runner.default.resolveBinary({
|
|
166
|
+
cwd,
|
|
167
|
+
fs: fs.default
|
|
168
|
+
});
|
|
59
169
|
}
|
|
60
170
|
function findConfigFile(cwd) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
return null;
|
|
171
|
+
return import_runner.default.resolveConfig({
|
|
172
|
+
cwd,
|
|
173
|
+
configPath,
|
|
174
|
+
fs: fs.default
|
|
175
|
+
});
|
|
67
176
|
}
|
|
68
177
|
function buildCSS(cwd, binaryPath) {
|
|
69
178
|
const resolvedConfigPath = findConfigFile(cwd);
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
let tempConfigPath = null;
|
|
77
|
-
if (resolvedConfigPath) args.push("--config", resolvedConfigPath);
|
|
78
|
-
else {
|
|
79
|
-
tempConfigPath = path.default.join(cwd, ".gustcss.vite.tmp.json");
|
|
80
|
-
const tempConfig = { content };
|
|
81
|
-
fs.default.writeFileSync(tempConfigPath, JSON.stringify(tempConfig));
|
|
82
|
-
args.push("--config", tempConfigPath);
|
|
83
|
-
}
|
|
84
|
-
try {
|
|
85
|
-
const result = (0, child_process.spawnSync)(binaryPath, args, {
|
|
86
|
-
cwd,
|
|
87
|
-
encoding: "utf-8",
|
|
88
|
-
stdio: [
|
|
89
|
-
"pipe",
|
|
90
|
-
"pipe",
|
|
91
|
-
"pipe"
|
|
92
|
-
]
|
|
179
|
+
const run = (cfgPath) => {
|
|
180
|
+
const args = import_runner.default.buildArgs({
|
|
181
|
+
mode: "output",
|
|
182
|
+
output,
|
|
183
|
+
cssLayers: outputToCssLayers,
|
|
184
|
+
configPath: cfgPath
|
|
93
185
|
});
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
186
|
+
try {
|
|
187
|
+
const result = (0, child_process.spawnSync)(binaryPath, args, {
|
|
188
|
+
cwd,
|
|
189
|
+
encoding: "utf-8",
|
|
190
|
+
stdio: [
|
|
191
|
+
"pipe",
|
|
192
|
+
"pipe",
|
|
193
|
+
"pipe"
|
|
194
|
+
]
|
|
195
|
+
});
|
|
196
|
+
if (result.status !== 0) throw new Error(`gustcss build failed: ${result.stderr}`);
|
|
197
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
198
|
+
} catch (error) {
|
|
199
|
+
throw new Error(`gustcss build failed: ${error.message}`);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
if (resolvedConfigPath) run(resolvedConfigPath);
|
|
203
|
+
else import_runner.default.withTempConfig({
|
|
204
|
+
cwd,
|
|
205
|
+
content,
|
|
206
|
+
prefix: ".gustcss.vite",
|
|
207
|
+
fs: fs.default
|
|
208
|
+
}, (tempConfigPath) => run(tempConfigPath));
|
|
101
209
|
}
|
|
102
210
|
return {
|
|
103
211
|
name: "gustcss",
|
|
@@ -121,22 +229,21 @@ function cssUtility(opts = {}) {
|
|
|
121
229
|
} catch (error) {
|
|
122
230
|
console.error(`[gustcss] Failed to build CSS: ${error.message}`);
|
|
123
231
|
}
|
|
124
|
-
const watchArgs = [
|
|
125
|
-
"build",
|
|
126
|
-
"--watch",
|
|
127
|
-
"-o",
|
|
128
|
-
output
|
|
129
|
-
];
|
|
130
|
-
if (outputToCssLayers) watchArgs.push("--css-layers");
|
|
131
232
|
let tempConfigPath = null;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
fs.default.writeFileSync(tempConfigPath, JSON.stringify(
|
|
137
|
-
|
|
233
|
+
let watchConfigPath = resolvedConfigPath;
|
|
234
|
+
if (!watchConfigPath) {
|
|
235
|
+
const randomHex = (0, crypto.randomBytes)(8).toString("hex");
|
|
236
|
+
tempConfigPath = path.default.join(cwd, `.gustcss.vite.${randomHex}.tmp.json`);
|
|
237
|
+
fs.default.writeFileSync(tempConfigPath, JSON.stringify({ content }), { flag: "wx" });
|
|
238
|
+
watchConfigPath = tempConfigPath;
|
|
138
239
|
}
|
|
139
|
-
watchProcess = (0, child_process.spawn)(binaryPath,
|
|
240
|
+
watchProcess = (0, child_process.spawn)(binaryPath, import_runner.default.buildArgs({
|
|
241
|
+
mode: "output",
|
|
242
|
+
output,
|
|
243
|
+
cssLayers: outputToCssLayers,
|
|
244
|
+
configPath: watchConfigPath,
|
|
245
|
+
watch: true
|
|
246
|
+
}), {
|
|
140
247
|
cwd,
|
|
141
248
|
stdio: [
|
|
142
249
|
"pipe",
|
|
@@ -178,8 +285,6 @@ function cssUtility(opts = {}) {
|
|
|
178
285
|
}
|
|
179
286
|
};
|
|
180
287
|
}
|
|
181
|
-
var src_default = cssUtility;
|
|
182
|
-
|
|
183
288
|
//#endregion
|
|
184
289
|
exports.cssUtility = cssUtility;
|
|
185
|
-
exports.default =
|
|
290
|
+
exports.default = cssUtility;
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,142 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
1
2
|
import { spawn, spawnSync } from "child_process";
|
|
2
3
|
import path from "path";
|
|
3
4
|
import fs from "fs";
|
|
4
|
-
|
|
5
|
+
import { randomBytes } from "crypto";
|
|
6
|
+
//#region \0rolldown/runtime.js
|
|
7
|
+
var __create = Object.create;
|
|
8
|
+
var __defProp = Object.defineProperty;
|
|
9
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
10
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
11
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
12
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
13
|
+
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
14
|
+
var __copyProps = (to, from, except, desc) => {
|
|
15
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
16
|
+
key = keys[i];
|
|
17
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
18
|
+
get: ((k) => from[k]).bind(null, key),
|
|
19
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
25
|
+
value: mod,
|
|
26
|
+
enumerable: true
|
|
27
|
+
}) : target, mod));
|
|
28
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
29
|
+
//#endregion
|
|
5
30
|
//#region src/index.js
|
|
31
|
+
var import_runner = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
32
|
+
/**
|
|
33
|
+
* gustcss shared runner — helpers for the @gustcss/postcss and @gustcss/vite
|
|
34
|
+
* plugins.
|
|
35
|
+
*
|
|
36
|
+
* These functions centralize the four pieces of logic that the two plugins
|
|
37
|
+
* previously duplicated:
|
|
38
|
+
* - binary discovery (resolveBinary)
|
|
39
|
+
* - config-file detection (resolveConfig)
|
|
40
|
+
* - temporary config-file lifecycle (withTempConfig)
|
|
41
|
+
* - CLI argument assembly (buildArgs)
|
|
42
|
+
*
|
|
43
|
+
* They are pure (no module-level state) and accept `fs` via dependency
|
|
44
|
+
* injection so callers and tests can substitute it. The implementation uses
|
|
45
|
+
* only Node's built-in `path`, `fs`, and `crypto` — no third-party dependencies.
|
|
46
|
+
*
|
|
47
|
+
* This module is NOT a public API. It is inlined into each plugin's `dist`
|
|
48
|
+
* bundle at build time (rolldown) and is never published as a standalone
|
|
49
|
+
* source file. The `npm/shared/` directory deliberately has no own
|
|
50
|
+
* package.json with a public name, only a private one for running tests.
|
|
51
|
+
*
|
|
52
|
+
* @security Paths (cwd, config, output) are trusted: they originate from the
|
|
53
|
+
* developer's build configuration. These helpers are for build-time use only.
|
|
54
|
+
*/
|
|
55
|
+
const path$1 = __require("path");
|
|
56
|
+
const crypto = __require("crypto");
|
|
57
|
+
const DEFAULT_CONFIG_NAMES = ["gustcss.config.json", "gustcss.config.js"];
|
|
58
|
+
/**
|
|
59
|
+
* Locate the gustcss CLI binary, trying three paths in order:
|
|
60
|
+
* 1. <cwd>/node_modules/.bin/gustcss (normal local install)
|
|
61
|
+
* 2. <cwd>/../../bin/gustcss (sample projects in this repo)
|
|
62
|
+
* 3. 'gustcss' (global install — always accepted)
|
|
63
|
+
*
|
|
64
|
+
* @param {{ cwd: string, fs?: { existsSync: Function } }} options
|
|
65
|
+
* @returns {string} the resolved binary path or the bare 'gustcss' sentinel
|
|
66
|
+
*/
|
|
67
|
+
function resolveBinary({ cwd, fs = __require("fs") }) {
|
|
68
|
+
const candidates = [path$1.join(cwd, "node_modules", ".bin", "gustcss"), path$1.join(cwd, "..", "..", "bin", "gustcss")];
|
|
69
|
+
for (const candidate of candidates) if (fs.existsSync(candidate)) return candidate;
|
|
70
|
+
return "gustcss";
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Determine which config file the CLI should use.
|
|
74
|
+
* - If an explicit configPath is given, it is returned as-is (no fs access).
|
|
75
|
+
* - Otherwise auto-detect gustcss.config.json, then gustcss.config.js, in cwd.
|
|
76
|
+
* - Returns null when nothing is found (caller falls back to a temp config).
|
|
77
|
+
*
|
|
78
|
+
* @param {{ cwd: string, configPath?: string, fs?: { existsSync: Function } }} options
|
|
79
|
+
* @returns {string|null}
|
|
80
|
+
*/
|
|
81
|
+
function resolveConfig({ cwd, configPath, fs = __require("fs") }) {
|
|
82
|
+
if (configPath) return configPath;
|
|
83
|
+
for (const name of DEFAULT_CONFIG_NAMES) {
|
|
84
|
+
const fullPath = path$1.join(cwd, name);
|
|
85
|
+
if (fs.existsSync(fullPath)) return fullPath;
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Write a temporary config file ({ content }) at <cwd>/<prefix>.<random8hex>.tmp.json,
|
|
91
|
+
* invoke `fn` with the temp file path, and always clean the file up afterwards.
|
|
92
|
+
*
|
|
93
|
+
* The file is created with the 'wx' flag (O_EXCL equivalent): if a file with
|
|
94
|
+
* the same name already exists the write fails, preventing symlink-redirection
|
|
95
|
+
* attacks and parallel-run collisions. Because the name contains 8 random bytes
|
|
96
|
+
* the probability of a collision is negligible.
|
|
97
|
+
*
|
|
98
|
+
* @param {{ cwd: string, content: any, prefix: string, fs?: { writeFileSync: Function, existsSync: Function, unlinkSync: Function } }} options
|
|
99
|
+
* @param {(tempConfigPath: string) => any} fn
|
|
100
|
+
* @returns {any} whatever `fn` returns
|
|
101
|
+
*/
|
|
102
|
+
function withTempConfig({ cwd, content, prefix, fs = __require("fs") }, fn) {
|
|
103
|
+
const tmpName = `${prefix}.${crypto.randomBytes(8).toString("hex")}.tmp.json`;
|
|
104
|
+
const tempConfigPath = path$1.join(cwd, tmpName);
|
|
105
|
+
fs.writeFileSync(tempConfigPath, JSON.stringify({ content }), { flag: "wx" });
|
|
106
|
+
try {
|
|
107
|
+
return fn(tempConfigPath);
|
|
108
|
+
} finally {
|
|
109
|
+
if (fs.existsSync(tempConfigPath)) fs.unlinkSync(tempConfigPath);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Assemble the CLI argument array.
|
|
114
|
+
*
|
|
115
|
+
* @param {object} options
|
|
116
|
+
* @param {'stdout'|'output'} options.mode 'stdout' emits to stdout (postcss);
|
|
117
|
+
* 'output' writes to a file (-o, vite).
|
|
118
|
+
* @param {string} [options.output] output path (required for 'output' mode)
|
|
119
|
+
* @param {boolean} [options.cssLayers] append --css-layers when truthy
|
|
120
|
+
* @param {boolean} [options.watch] append --watch when truthy
|
|
121
|
+
* @param {string|null} [options.configPath] append --config <path> when present
|
|
122
|
+
* @returns {string[]}
|
|
123
|
+
*/
|
|
124
|
+
function buildArgs({ mode, output, cssLayers, configPath, watch }) {
|
|
125
|
+
const args = ["build"];
|
|
126
|
+
if (watch) args.push("--watch");
|
|
127
|
+
if (mode === "stdout") args.push("--stdout");
|
|
128
|
+
else args.push("-o", output);
|
|
129
|
+
if (cssLayers) args.push("--css-layers");
|
|
130
|
+
if (configPath) args.push("--config", configPath);
|
|
131
|
+
return args;
|
|
132
|
+
}
|
|
133
|
+
module.exports = {
|
|
134
|
+
resolveBinary,
|
|
135
|
+
resolveConfig,
|
|
136
|
+
withTempConfig,
|
|
137
|
+
buildArgs
|
|
138
|
+
};
|
|
139
|
+
})))(), 1);
|
|
6
140
|
/**
|
|
7
141
|
* @gustcss/vite - Vite plugin for CSS Utility Generator
|
|
8
142
|
*
|
|
@@ -11,6 +145,10 @@ import fs from "fs";
|
|
|
11
145
|
* export default defineConfig({
|
|
12
146
|
* plugins: [gustcss({ output: 'src/styles/utility.css' })],
|
|
13
147
|
* })
|
|
148
|
+
*
|
|
149
|
+
* @security Configuration paths (content, config, output) are trusted as they
|
|
150
|
+
* are provided by the developer in their build configuration. This plugin is
|
|
151
|
+
* designed for build-time use only and should not process untrusted input.
|
|
14
152
|
*/
|
|
15
153
|
function cssUtility(opts = {}) {
|
|
16
154
|
const output = opts.output || "src/styles/utility.css";
|
|
@@ -19,55 +157,50 @@ function cssUtility(opts = {}) {
|
|
|
19
157
|
const outputToCssLayers = opts.outputToCssLayers;
|
|
20
158
|
let watchProcess = null;
|
|
21
159
|
function findBinary(cwd) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
];
|
|
27
|
-
for (const p of possiblePaths) if (p === "gustcss" || fs.existsSync(p)) return p;
|
|
28
|
-
throw new Error("gustcss binary not found");
|
|
160
|
+
return import_runner.default.resolveBinary({
|
|
161
|
+
cwd,
|
|
162
|
+
fs
|
|
163
|
+
});
|
|
29
164
|
}
|
|
30
165
|
function findConfigFile(cwd) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
return null;
|
|
166
|
+
return import_runner.default.resolveConfig({
|
|
167
|
+
cwd,
|
|
168
|
+
configPath,
|
|
169
|
+
fs
|
|
170
|
+
});
|
|
37
171
|
}
|
|
38
172
|
function buildCSS(cwd, binaryPath) {
|
|
39
173
|
const resolvedConfigPath = findConfigFile(cwd);
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
let tempConfigPath = null;
|
|
47
|
-
if (resolvedConfigPath) args.push("--config", resolvedConfigPath);
|
|
48
|
-
else {
|
|
49
|
-
tempConfigPath = path.join(cwd, ".gustcss.vite.tmp.json");
|
|
50
|
-
const tempConfig = { content };
|
|
51
|
-
fs.writeFileSync(tempConfigPath, JSON.stringify(tempConfig));
|
|
52
|
-
args.push("--config", tempConfigPath);
|
|
53
|
-
}
|
|
54
|
-
try {
|
|
55
|
-
const result = spawnSync(binaryPath, args, {
|
|
56
|
-
cwd,
|
|
57
|
-
encoding: "utf-8",
|
|
58
|
-
stdio: [
|
|
59
|
-
"pipe",
|
|
60
|
-
"pipe",
|
|
61
|
-
"pipe"
|
|
62
|
-
]
|
|
174
|
+
const run = (cfgPath) => {
|
|
175
|
+
const args = import_runner.default.buildArgs({
|
|
176
|
+
mode: "output",
|
|
177
|
+
output,
|
|
178
|
+
cssLayers: outputToCssLayers,
|
|
179
|
+
configPath: cfgPath
|
|
63
180
|
});
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
181
|
+
try {
|
|
182
|
+
const result = spawnSync(binaryPath, args, {
|
|
183
|
+
cwd,
|
|
184
|
+
encoding: "utf-8",
|
|
185
|
+
stdio: [
|
|
186
|
+
"pipe",
|
|
187
|
+
"pipe",
|
|
188
|
+
"pipe"
|
|
189
|
+
]
|
|
190
|
+
});
|
|
191
|
+
if (result.status !== 0) throw new Error(`gustcss build failed: ${result.stderr}`);
|
|
192
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
193
|
+
} catch (error) {
|
|
194
|
+
throw new Error(`gustcss build failed: ${error.message}`);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
if (resolvedConfigPath) run(resolvedConfigPath);
|
|
198
|
+
else import_runner.default.withTempConfig({
|
|
199
|
+
cwd,
|
|
200
|
+
content,
|
|
201
|
+
prefix: ".gustcss.vite",
|
|
202
|
+
fs
|
|
203
|
+
}, (tempConfigPath) => run(tempConfigPath));
|
|
71
204
|
}
|
|
72
205
|
return {
|
|
73
206
|
name: "gustcss",
|
|
@@ -91,22 +224,21 @@ function cssUtility(opts = {}) {
|
|
|
91
224
|
} catch (error) {
|
|
92
225
|
console.error(`[gustcss] Failed to build CSS: ${error.message}`);
|
|
93
226
|
}
|
|
94
|
-
const watchArgs = [
|
|
95
|
-
"build",
|
|
96
|
-
"--watch",
|
|
97
|
-
"-o",
|
|
98
|
-
output
|
|
99
|
-
];
|
|
100
|
-
if (outputToCssLayers) watchArgs.push("--css-layers");
|
|
101
227
|
let tempConfigPath = null;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
fs.writeFileSync(tempConfigPath, JSON.stringify(
|
|
107
|
-
|
|
228
|
+
let watchConfigPath = resolvedConfigPath;
|
|
229
|
+
if (!watchConfigPath) {
|
|
230
|
+
const randomHex = randomBytes(8).toString("hex");
|
|
231
|
+
tempConfigPath = path.join(cwd, `.gustcss.vite.${randomHex}.tmp.json`);
|
|
232
|
+
fs.writeFileSync(tempConfigPath, JSON.stringify({ content }), { flag: "wx" });
|
|
233
|
+
watchConfigPath = tempConfigPath;
|
|
108
234
|
}
|
|
109
|
-
watchProcess = spawn(binaryPath,
|
|
235
|
+
watchProcess = spawn(binaryPath, import_runner.default.buildArgs({
|
|
236
|
+
mode: "output",
|
|
237
|
+
output,
|
|
238
|
+
cssLayers: outputToCssLayers,
|
|
239
|
+
configPath: watchConfigPath,
|
|
240
|
+
watch: true
|
|
241
|
+
}), {
|
|
110
242
|
cwd,
|
|
111
243
|
stdio: [
|
|
112
244
|
"pipe",
|
|
@@ -148,7 +280,5 @@ function cssUtility(opts = {}) {
|
|
|
148
280
|
}
|
|
149
281
|
};
|
|
150
282
|
}
|
|
151
|
-
var src_default = cssUtility;
|
|
152
|
-
|
|
153
283
|
//#endregion
|
|
154
|
-
export { cssUtility,
|
|
284
|
+
export { cssUtility, cssUtility as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gustcss/vite",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "Vite plugin for GustCSS",
|
|
5
5
|
"main": "dist/index.mjs",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "node build.js",
|
|
22
|
-
"prepublishOnly": "npm run build"
|
|
22
|
+
"prepublishOnly": "npm run build",
|
|
23
|
+
"test": "vitest run"
|
|
23
24
|
},
|
|
24
25
|
"keywords": [
|
|
25
26
|
"vite",
|
|
@@ -33,10 +34,11 @@
|
|
|
33
34
|
"access": "public"
|
|
34
35
|
},
|
|
35
36
|
"dependencies": {
|
|
36
|
-
"gustcss": "^0.5.
|
|
37
|
+
"gustcss": "^0.5.3"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
|
-
"rolldown": "^1.
|
|
40
|
+
"rolldown": "^1.1.0",
|
|
41
|
+
"vitest": "^4.1.8"
|
|
40
42
|
},
|
|
41
43
|
"peerDependencies": {
|
|
42
44
|
"vite": "^4.0.0 || ^5.0.0 || ^6.0.0"
|