@gustcss/postcss 0.5.2 → 0.5.4

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 (3) hide show
  1. package/dist/index.cjs +193 -0
  2. package/package.json +24 -5
  3. package/index.js +0 -136
package/dist/index.cjs ADDED
@@ -0,0 +1,193 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
3
+ //#endregion
4
+ //#region ../shared/runner.js
5
+ var require_runner = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6
+ /**
7
+ * gustcss shared runner — helpers for the @gustcss/postcss and @gustcss/vite
8
+ * plugins.
9
+ *
10
+ * These functions centralize the four pieces of logic that the two plugins
11
+ * previously duplicated:
12
+ * - binary discovery (resolveBinary)
13
+ * - config-file detection (resolveConfig)
14
+ * - temporary config-file lifecycle (withTempConfig)
15
+ * - CLI argument assembly (buildArgs)
16
+ *
17
+ * They are pure (no module-level state) and accept `fs` via dependency
18
+ * injection so callers and tests can substitute it. The implementation uses
19
+ * only Node's built-in `path`, `fs`, and `crypto` — no third-party dependencies.
20
+ *
21
+ * This module is NOT a public API. It is inlined into each plugin's `dist`
22
+ * bundle at build time (rolldown) and is never published as a standalone
23
+ * source file. The `npm/shared/` directory deliberately has no own
24
+ * package.json with a public name, only a private one for running tests.
25
+ *
26
+ * @security Paths (cwd, config, output) are trusted: they originate from the
27
+ * developer's build configuration. These helpers are for build-time use only.
28
+ */
29
+ const path = require("path");
30
+ const crypto = require("crypto");
31
+ const DEFAULT_CONFIG_NAMES = ["gustcss.config.json", "gustcss.config.js"];
32
+ /**
33
+ * Locate the gustcss CLI binary, trying three paths in order:
34
+ * 1. <cwd>/node_modules/.bin/gustcss (normal local install)
35
+ * 2. <cwd>/../../bin/gustcss (sample projects in this repo)
36
+ * 3. 'gustcss' (global install — always accepted)
37
+ *
38
+ * @param {{ cwd: string, fs?: { existsSync: Function } }} options
39
+ * @returns {string} the resolved binary path or the bare 'gustcss' sentinel
40
+ */
41
+ function resolveBinary({ cwd, fs = require("fs") }) {
42
+ const candidates = [path.join(cwd, "node_modules", ".bin", "gustcss"), path.join(cwd, "..", "..", "bin", "gustcss")];
43
+ for (const candidate of candidates) if (fs.existsSync(candidate)) return candidate;
44
+ return "gustcss";
45
+ }
46
+ /**
47
+ * Determine which config file the CLI should use.
48
+ * - If an explicit configPath is given, it is returned as-is (no fs access).
49
+ * - Otherwise auto-detect gustcss.config.json, then gustcss.config.js, in cwd.
50
+ * - Returns null when nothing is found (caller falls back to a temp config).
51
+ *
52
+ * @param {{ cwd: string, configPath?: string, fs?: { existsSync: Function } }} options
53
+ * @returns {string|null}
54
+ */
55
+ function resolveConfig({ cwd, configPath, fs = require("fs") }) {
56
+ if (configPath) return configPath;
57
+ for (const name of DEFAULT_CONFIG_NAMES) {
58
+ const fullPath = path.join(cwd, name);
59
+ if (fs.existsSync(fullPath)) return fullPath;
60
+ }
61
+ return null;
62
+ }
63
+ /**
64
+ * Write a temporary config file ({ content }) at <cwd>/<prefix>.<random8hex>.tmp.json,
65
+ * invoke `fn` with the temp file path, and always clean the file up afterwards.
66
+ *
67
+ * The file is created with the 'wx' flag (O_EXCL equivalent): if a file with
68
+ * the same name already exists the write fails, preventing symlink-redirection
69
+ * attacks and parallel-run collisions. Because the name contains 8 random bytes
70
+ * the probability of a collision is negligible.
71
+ *
72
+ * @param {{ cwd: string, content: any, prefix: string, fs?: { writeFileSync: Function, existsSync: Function, unlinkSync: Function } }} options
73
+ * @param {(tempConfigPath: string) => any} fn
74
+ * @returns {any} whatever `fn` returns
75
+ */
76
+ function withTempConfig({ cwd, content, prefix, fs = require("fs") }, fn) {
77
+ const tmpName = `${prefix}.${crypto.randomBytes(8).toString("hex")}.tmp.json`;
78
+ const tempConfigPath = path.join(cwd, tmpName);
79
+ fs.writeFileSync(tempConfigPath, JSON.stringify({ content }), { flag: "wx" });
80
+ try {
81
+ return fn(tempConfigPath);
82
+ } finally {
83
+ if (fs.existsSync(tempConfigPath)) fs.unlinkSync(tempConfigPath);
84
+ }
85
+ }
86
+ /**
87
+ * Assemble the CLI argument array.
88
+ *
89
+ * @param {object} options
90
+ * @param {'stdout'|'output'} options.mode 'stdout' emits to stdout (postcss);
91
+ * 'output' writes to a file (-o, vite).
92
+ * @param {string} [options.output] output path (required for 'output' mode)
93
+ * @param {boolean} [options.cssLayers] append --css-layers when truthy
94
+ * @param {boolean} [options.watch] append --watch when truthy
95
+ * @param {string|null} [options.configPath] append --config <path> when present
96
+ * @returns {string[]}
97
+ */
98
+ function buildArgs({ mode, output, cssLayers, configPath, watch }) {
99
+ const args = ["build"];
100
+ if (watch) args.push("--watch");
101
+ if (mode === "stdout") args.push("--stdout");
102
+ else args.push("-o", output);
103
+ if (cssLayers) args.push("--css-layers");
104
+ if (configPath) args.push("--config", configPath);
105
+ return args;
106
+ }
107
+ module.exports = {
108
+ resolveBinary,
109
+ resolveConfig,
110
+ withTempConfig,
111
+ buildArgs
112
+ };
113
+ }));
114
+ //#endregion
115
+ //#region src/index.js
116
+ const childProcess = require("child_process");
117
+ const runner = require_runner();
118
+ /**
119
+ * @gustcss/postcss - PostCSS plugin for CSS Utility Generator
120
+ *
121
+ * Usage in postcss.config.js:
122
+ * module.exports = {
123
+ * plugins: [
124
+ * require('@gustcss/postcss')({
125
+ * content: ['./src/**\/*.{js,ts,jsx,tsx}'],
126
+ * }),
127
+ * ],
128
+ * }
129
+ *
130
+ * In your CSS file:
131
+ * @gustcss;
132
+ *
133
+ * @security Configuration paths (content, config) are trusted as they are
134
+ * provided by the developer in their build configuration. This plugin is
135
+ * designed for build-time use only and should not process untrusted input.
136
+ */
137
+ const plugin = (opts = {}) => {
138
+ const content = opts.content || ["./src/**/*.{js,ts,jsx,tsx}"];
139
+ const configPath = opts.config;
140
+ const outputToCssLayers = opts.outputToCssLayers;
141
+ return {
142
+ postcssPlugin: "gustcss",
143
+ Once(root, { result }) {
144
+ let directiveAtRule = null;
145
+ root.walkAtRules("gustcss", (atRule) => {
146
+ if (directiveAtRule === null) directiveAtRule = atRule;
147
+ else atRule.remove();
148
+ });
149
+ if (directiveAtRule === null) return;
150
+ const cwd = process.cwd();
151
+ const binaryPath = runner.resolveBinary({ cwd });
152
+ const resolvedConfigPath = runner.resolveConfig({
153
+ cwd,
154
+ configPath
155
+ });
156
+ const fromFile = root.source?.input?.file ?? result.opts?.from;
157
+ const run = (cfgPath) => {
158
+ const args = runner.buildArgs({
159
+ mode: "stdout",
160
+ cssLayers: outputToCssLayers,
161
+ configPath: cfgPath
162
+ });
163
+ try {
164
+ const spawnResult = childProcess.spawnSync(binaryPath, args, {
165
+ cwd,
166
+ encoding: "utf-8",
167
+ stdio: [
168
+ "pipe",
169
+ "pipe",
170
+ "pipe"
171
+ ]
172
+ });
173
+ if (spawnResult.status !== 0) throw new Error(`gustcss build failed: ${spawnResult.stderr}`);
174
+ if (spawnResult.stderr) process.stderr.write(spawnResult.stderr);
175
+ const parsed = require("postcss").parse(spawnResult.stdout, { from: fromFile });
176
+ if (directiveAtRule) directiveAtRule.replaceWith(parsed);
177
+ else root.append(parsed);
178
+ } catch (error) {
179
+ throw new Error(`gustcss build failed: ${error.message}`);
180
+ }
181
+ };
182
+ if (resolvedConfigPath) run(resolvedConfigPath);
183
+ else runner.withTempConfig({
184
+ cwd,
185
+ content,
186
+ prefix: ".gustcss.postcss"
187
+ }, (tempConfigPath) => run(tempConfigPath));
188
+ }
189
+ };
190
+ };
191
+ module.exports = plugin;
192
+ module.exports.postcss = true;
193
+ //#endregion
package/package.json CHANGED
@@ -1,21 +1,40 @@
1
1
  {
2
2
  "name": "@gustcss/postcss",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "PostCSS plugin for GustCSS",
5
- "main": "index.js",
5
+ "main": "dist/index.cjs",
6
6
  "type": "commonjs",
7
7
  "files": [
8
- "index.js"
8
+ "dist/index.cjs",
9
+ "README.md",
10
+ "LICENSE"
11
+ ],
12
+ "keywords": [
13
+ "postcss",
14
+ "postcss-plugin",
15
+ "gustcss",
16
+ "gust",
17
+ "css",
18
+ "utility"
9
19
  ],
10
- "keywords": ["postcss", "postcss-plugin", "gustcss", "gust", "css", "utility"],
11
20
  "publishConfig": {
12
21
  "access": "public"
13
22
  },
14
23
  "dependencies": {
15
- "gustcss": "^0.5.2"
24
+ "gustcss": "^0.5.4"
16
25
  },
17
26
  "peerDependencies": {
18
27
  "postcss": "^8.0.0"
19
28
  },
29
+ "devDependencies": {
30
+ "postcss": "^8.5.15",
31
+ "rolldown": "^1.1.0",
32
+ "vitest": "^4.1.8"
33
+ },
34
+ "scripts": {
35
+ "build": "node build.mjs",
36
+ "prepublishOnly": "npm run build",
37
+ "test": "vitest run"
38
+ },
20
39
  "license": "MIT"
21
40
  }
package/index.js DELETED
@@ -1,136 +0,0 @@
1
- const { spawnSync } = require('child_process')
2
- const path = require('path')
3
- const fs = require('fs')
4
-
5
- /**
6
- * @gustcss/postcss - PostCSS plugin for CSS Utility Generator
7
- *
8
- * Usage in postcss.config.js:
9
- * module.exports = {
10
- * plugins: [
11
- * require('@gustcss/postcss')({
12
- * content: ['./src/**\/*.{js,ts,jsx,tsx}'],
13
- * }),
14
- * ],
15
- * }
16
- *
17
- * In your CSS file:
18
- * @gustcss;
19
- *
20
- * @security Configuration paths (content, config) are trusted as they are
21
- * provided by the developer in their build configuration. This plugin is
22
- * designed for build-time use only and should not process untrusted input.
23
- */
24
- module.exports = (opts = {}) => {
25
- const content = opts.content || ['./src/**/*.{js,ts,jsx,tsx}']
26
- const configPath = opts.config
27
- const outputToCssLayers = opts.outputToCssLayers
28
-
29
- return {
30
- postcssPlugin: 'gustcss',
31
- Once(root, { result }) {
32
- let hasDirective = false
33
- root.walkAtRules('gustcss', (atRule) => {
34
- hasDirective = true
35
- atRule.remove()
36
- })
37
-
38
- if (!hasDirective) {
39
- return
40
- }
41
-
42
- // Find the CLI binary
43
- const cwd = process.cwd()
44
- const possiblePaths = [
45
- path.join(cwd, 'node_modules', '.bin', 'gustcss'),
46
- path.join(cwd, '..', '..', 'bin', 'gustcss'), // For sample projects
47
- 'gustcss', // Global installation
48
- ]
49
-
50
- let binaryPath = null
51
- for (const p of possiblePaths) {
52
- if (p === 'gustcss' || fs.existsSync(p)) {
53
- binaryPath = p
54
- break
55
- }
56
- }
57
-
58
- if (!binaryPath) {
59
- throw new Error('gustcss binary not found')
60
- }
61
-
62
- // Build CLI arguments
63
- const args = ['build', '--stdout']
64
-
65
- // Add --css-layers flag if enabled
66
- if (outputToCssLayers) {
67
- args.push('--css-layers')
68
- }
69
-
70
- // Determine config file path
71
- let resolvedConfigPath = configPath
72
- if (!resolvedConfigPath) {
73
- // Auto-detect config file in current directory
74
- const defaultConfigPaths = [
75
- 'gustcss.config.json',
76
- 'gustcss.config.js',
77
- ]
78
- for (const configName of defaultConfigPaths) {
79
- const fullPath = path.join(cwd, configName)
80
- if (fs.existsSync(fullPath)) {
81
- resolvedConfigPath = fullPath
82
- break
83
- }
84
- }
85
- }
86
-
87
- let tempConfigPath = null
88
-
89
- if (resolvedConfigPath) {
90
- // Use existing config file
91
- args.push('--config', resolvedConfigPath)
92
- } else {
93
- // Create temporary config file to avoid glob pattern issues with CLI
94
- tempConfigPath = path.join(cwd, '.gustcss.postcss.tmp.json')
95
- const tempConfig = { content }
96
- fs.writeFileSync(tempConfigPath, JSON.stringify(tempConfig))
97
- args.push('--config', tempConfigPath)
98
- }
99
-
100
- try {
101
- const result = spawnSync(binaryPath, args, {
102
- cwd,
103
- encoding: 'utf-8',
104
- stdio: ['pipe', 'pipe', 'pipe'],
105
- })
106
-
107
- if (result.status !== 0) {
108
- throw new Error(`gustcss build failed: ${result.stderr}`)
109
- }
110
-
111
- // Output generation time log from stderr
112
- if (result.stderr) {
113
- process.stderr.write(result.stderr)
114
- }
115
-
116
- // Parse the generated CSS and append to the root
117
- const postcss = require('postcss')
118
- const parsed = postcss.parse(result.stdout)
119
- root.append(parsed)
120
- } catch (error) {
121
- // CLI build failures (status !== 0) are already thrown above with stderr details.
122
- // This catch block handles other errors:
123
- // - spawnSync failures (e.g., binary not found, spawn error)
124
- // - postcss.parse errors (malformed CSS output)
125
- throw new Error(`gustcss build failed: ${error.message}`)
126
- } finally {
127
- // Clean up temporary config file
128
- if (tempConfigPath && fs.existsSync(tempConfigPath)) {
129
- fs.unlinkSync(tempConfigPath)
130
- }
131
- }
132
- },
133
- }
134
- }
135
-
136
- module.exports.postcss = true