vite-plugin-qingkuai 1.0.24 → 1.0.26
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 +60 -46
- package/dist/index.js +60 -46
- package/package.json +1 -1
- package/.github/workflows/release.yml +0 -106
- package/.husky/pre-commit +0 -1
- package/scripts/release.js +0 -94
package/dist/index.cjs
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
var vite = require('vite');
|
|
4
4
|
var nodeFs = require('node:fs');
|
|
5
|
+
var nodeUrl = require('node:url');
|
|
5
6
|
var nodePath = require('node:path');
|
|
6
7
|
var nodeCrypto = require('node:crypto');
|
|
7
|
-
var compiler = require('qingkuai/compiler');
|
|
8
8
|
var linesAndColumns = require('lines-and-columns');
|
|
9
9
|
var sourcemapCodec = require('@jridgewell/sourcemap-codec');
|
|
10
10
|
var postcss = require('postcss');
|
|
11
11
|
var selectorParser = require('postcss-selector-parser');
|
|
12
|
+
var compiler = require('qingkuai/compiler');
|
|
12
13
|
var sourceMapJs = require('source-map-js');
|
|
13
14
|
|
|
14
15
|
function _interopNamespaceDefault(e) {
|
|
@@ -30,29 +31,6 @@ function _interopNamespaceDefault(e) {
|
|
|
30
31
|
|
|
31
32
|
var vite__namespace = /*#__PURE__*/_interopNamespaceDefault(vite);
|
|
32
33
|
|
|
33
|
-
function isNumber(v) {
|
|
34
|
-
return typeof v === "number";
|
|
35
|
-
}
|
|
36
|
-
function isUndefined(v) {
|
|
37
|
-
return void 0 === v;
|
|
38
|
-
}
|
|
39
|
-
function findFilesByName(dir, targetFileName, ignoreList) {
|
|
40
|
-
const result = [];
|
|
41
|
-
const entries = nodeFs.readdirSync(dir, { withFileTypes: true });
|
|
42
|
-
for (const entry of entries) {
|
|
43
|
-
if (ignoreList.has(entry.name)) {
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
const fullPath = nodePath.join(dir, entry.name);
|
|
47
|
-
if (entry.name === targetFileName) {
|
|
48
|
-
result.push(fullPath);
|
|
49
|
-
} else if (entry.isDirectory()) {
|
|
50
|
-
result.push(...findFilesByName(fullPath, targetFileName, ignoreList));
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return result;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
34
|
async function attachScopeForStyleSelectors(code, hash, sourceFile, map) {
|
|
57
35
|
let error;
|
|
58
36
|
const processedRules = /* @__PURE__ */ new WeakSet();
|
|
@@ -126,10 +104,34 @@ const globalStyle = "\n" + compiler.util.formatSourceCode(
|
|
|
126
104
|
).replace(/^/gm, " ") + "\n";
|
|
127
105
|
const VIRTUAL_STYLE_ID_RE = /^virtual:\[\d+\].*?\.qk\.(css|s[ac]ss|less|stylus|postcss)\?\d{13}$/;
|
|
128
106
|
|
|
129
|
-
function
|
|
107
|
+
function isNumber(v) {
|
|
108
|
+
return typeof v === "number";
|
|
109
|
+
}
|
|
110
|
+
function isUndefined(v) {
|
|
111
|
+
return void 0 === v;
|
|
112
|
+
}
|
|
113
|
+
function findFilesByName(dir, targetFileName, ignoreList) {
|
|
114
|
+
const result = [];
|
|
115
|
+
const entries = nodeFs.readdirSync(dir, { withFileTypes: true });
|
|
116
|
+
for (const entry of entries) {
|
|
117
|
+
if (ignoreList.has(entry.name)) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const fullPath = nodePath.join(dir, entry.name);
|
|
121
|
+
if (entry.name === targetFileName) {
|
|
122
|
+
result.push(fullPath);
|
|
123
|
+
} else if (entry.isDirectory()) {
|
|
124
|
+
result.push(...findFilesByName(fullPath, targetFileName, ignoreList));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function offsetSourceMap(sourceIndices, mappings, preLine, preColumn) {
|
|
131
|
+
const indices = new Set(sourceIndices);
|
|
130
132
|
return sourcemapCodec.decode(mappings).map((line) => {
|
|
131
133
|
return line.map((segment) => {
|
|
132
|
-
if (segment.length === 1 || segment[1]
|
|
134
|
+
if (segment.length === 1 || !indices.has(segment[1])) {
|
|
133
135
|
return segment;
|
|
134
136
|
}
|
|
135
137
|
if (segment[2] === preLine) {
|
|
@@ -273,9 +275,11 @@ Caused by: "${fileId}".`;
|
|
|
273
275
|
}
|
|
274
276
|
return this.error(err);
|
|
275
277
|
}
|
|
278
|
+
preprocessRes.deps?.forEach((dep) => this.addWatchFile(dep));
|
|
276
279
|
if (!cssSourcemap || styleDescriptor.global) {
|
|
277
280
|
return preprocessRes;
|
|
278
281
|
}
|
|
282
|
+
const phantomIndices = [];
|
|
279
283
|
const assertedPreprocessMap = preprocessRes.map;
|
|
280
284
|
const attachScopeResult = await attachScopeForStyleSelectors(
|
|
281
285
|
preprocessRes.code,
|
|
@@ -284,15 +288,28 @@ Caused by: "${fileId}".`;
|
|
|
284
288
|
assertedPreprocessMap
|
|
285
289
|
);
|
|
286
290
|
const attachScopeMap = attachScopeResult.map ?? assertedPreprocessMap;
|
|
287
|
-
const
|
|
288
|
-
|
|
291
|
+
const sameVirtualSource = (source) => {
|
|
292
|
+
let resolved = source.startsWith("file://") ? nodeUrl.fileURLToPath(source) : source;
|
|
293
|
+
if (!nodePath.isAbsolute(resolved)) {
|
|
294
|
+
const candidates = [
|
|
295
|
+
nodePath.resolve(nodePath.dirname(virtualFileName), resolved),
|
|
296
|
+
nodePath.resolve(resolved)
|
|
297
|
+
];
|
|
298
|
+
return candidates.some(
|
|
299
|
+
(candidate) => nodePath.normalize(candidate) === nodePath.normalize(virtualFileName)
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
return nodePath.normalize(resolved) === nodePath.normalize(virtualFileName);
|
|
289
303
|
};
|
|
290
|
-
|
|
291
|
-
|
|
304
|
+
attachScopeMap?.sources.forEach((source, index2) => {
|
|
305
|
+
if (sameVirtualSource(source)) {
|
|
306
|
+
phantomIndices.push(index2);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
292
309
|
const offsetMappings = sourcemapCodec.encode(
|
|
293
310
|
offsetSourceMap(
|
|
311
|
+
phantomIndices,
|
|
294
312
|
attachScopeResult.mappings,
|
|
295
|
-
currentSourceIndex,
|
|
296
313
|
styleDescriptor.loc.start.line - 1,
|
|
297
314
|
styleDescriptor.loc.start.column - 1
|
|
298
315
|
)
|
|
@@ -306,7 +323,7 @@ Caused by: "${fileId}".`;
|
|
|
306
323
|
attachScopeResult.error.loc.line,
|
|
307
324
|
attachScopeResult.error.loc.column
|
|
308
325
|
);
|
|
309
|
-
if (preprocessedPosition.source && !
|
|
326
|
+
if (preprocessedPosition.source && !sameVirtualSource(preprocessedPosition.source)) {
|
|
310
327
|
this.error({
|
|
311
328
|
message: attachScopeResult.error.message,
|
|
312
329
|
loc: {
|
|
@@ -337,20 +354,17 @@ Caused by: "${fileId}".`;
|
|
|
337
354
|
version: 3,
|
|
338
355
|
mappings: offsetMappings,
|
|
339
356
|
names: attachScopeMap?.names || assertedPreprocessMap?.names || [],
|
|
340
|
-
sources: attachScopeMap?.sources
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
352
|
-
return sourcesContent;
|
|
353
|
-
})() : void 0
|
|
357
|
+
sources: attachScopeMap?.sources.map((source, index2) => {
|
|
358
|
+
return phantomIndices.includes(index2) ? fileId : source;
|
|
359
|
+
}) ?? [
|
|
360
|
+
...assertedPreprocessMap?.sources.filter((source) => {
|
|
361
|
+
return !sameVirtualSource(source);
|
|
362
|
+
}) ?? [],
|
|
363
|
+
fileId
|
|
364
|
+
],
|
|
365
|
+
sourcesContent: attachScopeMap?.sourcesContent?.map((content, index2) => {
|
|
366
|
+
return phantomIndices.includes(index2) ? nodeFs.readFileSync(fileId, "utf-8") : content;
|
|
367
|
+
})
|
|
354
368
|
}
|
|
355
369
|
};
|
|
356
370
|
},
|
package/dist/index.js
CHANGED
|
@@ -1,37 +1,15 @@
|
|
|
1
1
|
import * as vite from 'vite';
|
|
2
2
|
import nodeFs from 'node:fs';
|
|
3
|
+
import nodeUrl from 'node:url';
|
|
3
4
|
import nodePath from 'node:path';
|
|
4
5
|
import nodeCrypto from 'node:crypto';
|
|
5
|
-
import { util, compile, isCompileError } from 'qingkuai/compiler';
|
|
6
6
|
import { LinesAndColumns } from 'lines-and-columns';
|
|
7
7
|
import { decode, encode } from '@jridgewell/sourcemap-codec';
|
|
8
8
|
import postcss from 'postcss';
|
|
9
9
|
import selectorParser from 'postcss-selector-parser';
|
|
10
|
+
import { util, compile, isCompileError } from 'qingkuai/compiler';
|
|
10
11
|
import { SourceMapConsumer } from 'source-map-js';
|
|
11
12
|
|
|
12
|
-
function isNumber(v) {
|
|
13
|
-
return typeof v === "number";
|
|
14
|
-
}
|
|
15
|
-
function isUndefined(v) {
|
|
16
|
-
return void 0 === v;
|
|
17
|
-
}
|
|
18
|
-
function findFilesByName(dir, targetFileName, ignoreList) {
|
|
19
|
-
const result = [];
|
|
20
|
-
const entries = nodeFs.readdirSync(dir, { withFileTypes: true });
|
|
21
|
-
for (const entry of entries) {
|
|
22
|
-
if (ignoreList.has(entry.name)) {
|
|
23
|
-
continue;
|
|
24
|
-
}
|
|
25
|
-
const fullPath = nodePath.join(dir, entry.name);
|
|
26
|
-
if (entry.name === targetFileName) {
|
|
27
|
-
result.push(fullPath);
|
|
28
|
-
} else if (entry.isDirectory()) {
|
|
29
|
-
result.push(...findFilesByName(fullPath, targetFileName, ignoreList));
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return result;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
13
|
async function attachScopeForStyleSelectors(code, hash, sourceFile, map) {
|
|
36
14
|
let error;
|
|
37
15
|
const processedRules = /* @__PURE__ */ new WeakSet();
|
|
@@ -105,10 +83,34 @@ const globalStyle = "\n" + util.formatSourceCode(
|
|
|
105
83
|
).replace(/^/gm, " ") + "\n";
|
|
106
84
|
const VIRTUAL_STYLE_ID_RE = /^virtual:\[\d+\].*?\.qk\.(css|s[ac]ss|less|stylus|postcss)\?\d{13}$/;
|
|
107
85
|
|
|
108
|
-
function
|
|
86
|
+
function isNumber(v) {
|
|
87
|
+
return typeof v === "number";
|
|
88
|
+
}
|
|
89
|
+
function isUndefined(v) {
|
|
90
|
+
return void 0 === v;
|
|
91
|
+
}
|
|
92
|
+
function findFilesByName(dir, targetFileName, ignoreList) {
|
|
93
|
+
const result = [];
|
|
94
|
+
const entries = nodeFs.readdirSync(dir, { withFileTypes: true });
|
|
95
|
+
for (const entry of entries) {
|
|
96
|
+
if (ignoreList.has(entry.name)) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const fullPath = nodePath.join(dir, entry.name);
|
|
100
|
+
if (entry.name === targetFileName) {
|
|
101
|
+
result.push(fullPath);
|
|
102
|
+
} else if (entry.isDirectory()) {
|
|
103
|
+
result.push(...findFilesByName(fullPath, targetFileName, ignoreList));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function offsetSourceMap(sourceIndices, mappings, preLine, preColumn) {
|
|
110
|
+
const indices = new Set(sourceIndices);
|
|
109
111
|
return decode(mappings).map((line) => {
|
|
110
112
|
return line.map((segment) => {
|
|
111
|
-
if (segment.length === 1 || segment[1]
|
|
113
|
+
if (segment.length === 1 || !indices.has(segment[1])) {
|
|
112
114
|
return segment;
|
|
113
115
|
}
|
|
114
116
|
if (segment[2] === preLine) {
|
|
@@ -252,9 +254,11 @@ Caused by: "${fileId}".`;
|
|
|
252
254
|
}
|
|
253
255
|
return this.error(err);
|
|
254
256
|
}
|
|
257
|
+
preprocessRes.deps?.forEach((dep) => this.addWatchFile(dep));
|
|
255
258
|
if (!cssSourcemap || styleDescriptor.global) {
|
|
256
259
|
return preprocessRes;
|
|
257
260
|
}
|
|
261
|
+
const phantomIndices = [];
|
|
258
262
|
const assertedPreprocessMap = preprocessRes.map;
|
|
259
263
|
const attachScopeResult = await attachScopeForStyleSelectors(
|
|
260
264
|
preprocessRes.code,
|
|
@@ -263,15 +267,28 @@ Caused by: "${fileId}".`;
|
|
|
263
267
|
assertedPreprocessMap
|
|
264
268
|
);
|
|
265
269
|
const attachScopeMap = attachScopeResult.map ?? assertedPreprocessMap;
|
|
266
|
-
const
|
|
267
|
-
|
|
270
|
+
const sameVirtualSource = (source) => {
|
|
271
|
+
let resolved = source.startsWith("file://") ? nodeUrl.fileURLToPath(source) : source;
|
|
272
|
+
if (!nodePath.isAbsolute(resolved)) {
|
|
273
|
+
const candidates = [
|
|
274
|
+
nodePath.resolve(nodePath.dirname(virtualFileName), resolved),
|
|
275
|
+
nodePath.resolve(resolved)
|
|
276
|
+
];
|
|
277
|
+
return candidates.some(
|
|
278
|
+
(candidate) => nodePath.normalize(candidate) === nodePath.normalize(virtualFileName)
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
return nodePath.normalize(resolved) === nodePath.normalize(virtualFileName);
|
|
268
282
|
};
|
|
269
|
-
|
|
270
|
-
|
|
283
|
+
attachScopeMap?.sources.forEach((source, index2) => {
|
|
284
|
+
if (sameVirtualSource(source)) {
|
|
285
|
+
phantomIndices.push(index2);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
271
288
|
const offsetMappings = encode(
|
|
272
289
|
offsetSourceMap(
|
|
290
|
+
phantomIndices,
|
|
273
291
|
attachScopeResult.mappings,
|
|
274
|
-
currentSourceIndex,
|
|
275
292
|
styleDescriptor.loc.start.line - 1,
|
|
276
293
|
styleDescriptor.loc.start.column - 1
|
|
277
294
|
)
|
|
@@ -285,7 +302,7 @@ Caused by: "${fileId}".`;
|
|
|
285
302
|
attachScopeResult.error.loc.line,
|
|
286
303
|
attachScopeResult.error.loc.column
|
|
287
304
|
);
|
|
288
|
-
if (preprocessedPosition.source && !
|
|
305
|
+
if (preprocessedPosition.source && !sameVirtualSource(preprocessedPosition.source)) {
|
|
289
306
|
this.error({
|
|
290
307
|
message: attachScopeResult.error.message,
|
|
291
308
|
loc: {
|
|
@@ -316,20 +333,17 @@ Caused by: "${fileId}".`;
|
|
|
316
333
|
version: 3,
|
|
317
334
|
mappings: offsetMappings,
|
|
318
335
|
names: attachScopeMap?.names || assertedPreprocessMap?.names || [],
|
|
319
|
-
sources: attachScopeMap?.sources
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
331
|
-
return sourcesContent;
|
|
332
|
-
})() : void 0
|
|
336
|
+
sources: attachScopeMap?.sources.map((source, index2) => {
|
|
337
|
+
return phantomIndices.includes(index2) ? fileId : source;
|
|
338
|
+
}) ?? [
|
|
339
|
+
...assertedPreprocessMap?.sources.filter((source) => {
|
|
340
|
+
return !sameVirtualSource(source);
|
|
341
|
+
}) ?? [],
|
|
342
|
+
fileId
|
|
343
|
+
],
|
|
344
|
+
sourcesContent: attachScopeMap?.sourcesContent?.map((content, index2) => {
|
|
345
|
+
return phantomIndices.includes(index2) ? nodeFs.readFileSync(fileId, "utf-8") : content;
|
|
346
|
+
})
|
|
333
347
|
}
|
|
334
348
|
};
|
|
335
349
|
},
|
package/package.json
CHANGED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
name: Release
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
tags:
|
|
6
|
-
- "v*"
|
|
7
|
-
workflow_dispatch:
|
|
8
|
-
inputs:
|
|
9
|
-
tag:
|
|
10
|
-
type: string
|
|
11
|
-
required: false
|
|
12
|
-
description: "Optional tag to release (for example: v1.0.66). Leave empty to auto-create from package version."
|
|
13
|
-
|
|
14
|
-
permissions:
|
|
15
|
-
contents: write
|
|
16
|
-
id-token: write
|
|
17
|
-
|
|
18
|
-
jobs:
|
|
19
|
-
release:
|
|
20
|
-
runs-on: ubuntu-latest
|
|
21
|
-
|
|
22
|
-
steps:
|
|
23
|
-
- name: Checkout source
|
|
24
|
-
uses: actions/checkout@v6
|
|
25
|
-
with:
|
|
26
|
-
fetch-depth: 0
|
|
27
|
-
|
|
28
|
-
- name: Resolve release tag
|
|
29
|
-
id: tag
|
|
30
|
-
env:
|
|
31
|
-
INPUT_TAG: ${{ inputs.tag }}
|
|
32
|
-
REF_NAME: ${{ github.ref_name }}
|
|
33
|
-
EVENT_NAME: ${{ github.event_name }}
|
|
34
|
-
run: |
|
|
35
|
-
if [[ "$EVENT_NAME" == "push" ]]; then
|
|
36
|
-
echo "value=$REF_NAME" >> "$GITHUB_OUTPUT"
|
|
37
|
-
exit 0
|
|
38
|
-
fi
|
|
39
|
-
|
|
40
|
-
if [[ -n "$INPUT_TAG" ]]; then
|
|
41
|
-
echo "value=$INPUT_TAG" >> "$GITHUB_OUTPUT"
|
|
42
|
-
exit 0
|
|
43
|
-
fi
|
|
44
|
-
|
|
45
|
-
package_version="$(node -p 'require("./package.json").version')"
|
|
46
|
-
echo "value=v$package_version" >> "$GITHUB_OUTPUT"
|
|
47
|
-
|
|
48
|
-
- name: Ensure tag exists on workflow dispatch
|
|
49
|
-
if: ${{ github.event_name == 'workflow_dispatch' }}
|
|
50
|
-
env:
|
|
51
|
-
TAG: ${{ steps.tag.outputs.value }}
|
|
52
|
-
run: |
|
|
53
|
-
git fetch origin --tags --prune
|
|
54
|
-
|
|
55
|
-
if git rev-parse --verify --quiet "refs/tags/$TAG" >/dev/null; then
|
|
56
|
-
echo "Tag already exists: $TAG"
|
|
57
|
-
else
|
|
58
|
-
git config user.name "github-actions[bot]"
|
|
59
|
-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
60
|
-
git tag "$TAG"
|
|
61
|
-
git push origin "$TAG"
|
|
62
|
-
echo "Created and pushed tag: $TAG"
|
|
63
|
-
fi
|
|
64
|
-
|
|
65
|
-
- name: Checkout resolved tag
|
|
66
|
-
env:
|
|
67
|
-
TAG: ${{ steps.tag.outputs.value }}
|
|
68
|
-
run: |
|
|
69
|
-
git fetch origin --tags --prune
|
|
70
|
-
git checkout "refs/tags/$TAG"
|
|
71
|
-
|
|
72
|
-
- name: Setup pnpm
|
|
73
|
-
uses: pnpm/action-setup@v6
|
|
74
|
-
|
|
75
|
-
- name: Setup Node.js
|
|
76
|
-
uses: actions/setup-node@v6
|
|
77
|
-
with:
|
|
78
|
-
node-version: 24
|
|
79
|
-
cache: pnpm
|
|
80
|
-
registry-url: https://registry.npmjs.org
|
|
81
|
-
|
|
82
|
-
- name: Install dependencies
|
|
83
|
-
run: pnpm install --frozen-lockfile --ignore-scripts
|
|
84
|
-
|
|
85
|
-
- name: Validate tag equals package version
|
|
86
|
-
env:
|
|
87
|
-
TAG: ${{ steps.tag.outputs.value }}
|
|
88
|
-
run: |
|
|
89
|
-
package_version="$(node -p 'require("./package.json").version')"
|
|
90
|
-
if [[ "v$package_version" != "$TAG" ]]; then
|
|
91
|
-
echo "Tag and package.json version mismatch: $TAG vs v$package_version"
|
|
92
|
-
exit 1
|
|
93
|
-
fi
|
|
94
|
-
|
|
95
|
-
- name: Build
|
|
96
|
-
run: pnpm build
|
|
97
|
-
|
|
98
|
-
- name: Publish package to npm (Trusted Publishing)
|
|
99
|
-
run: npm publish --access public --provenance
|
|
100
|
-
|
|
101
|
-
- name: Create GitHub Release
|
|
102
|
-
uses: softprops/action-gh-release@v2
|
|
103
|
-
with:
|
|
104
|
-
tag_name: ${{ steps.tag.outputs.value }}
|
|
105
|
-
name: ${{ steps.tag.outputs.value }}
|
|
106
|
-
body: "Release ${{ steps.tag.outputs.value }}"
|
package/.husky/pre-commit
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
pnpm lint-staged
|
package/scripts/release.js
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import nodeFs from "node:fs"
|
|
3
|
-
import nodePath from "node:path"
|
|
4
|
-
|
|
5
|
-
import { spawnSync } from "node:child_process"
|
|
6
|
-
|
|
7
|
-
const ROOT = nodePath.resolve(process.cwd())
|
|
8
|
-
const PKG_PATH = nodePath.join(ROOT, "package.json")
|
|
9
|
-
|
|
10
|
-
main()
|
|
11
|
-
|
|
12
|
-
function readPackageJson() {
|
|
13
|
-
return JSON.parse(nodeFs.readFileSync(PKG_PATH, "utf-8"))
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function writePackageJson(pkg) {
|
|
17
|
-
nodeFs.writeFileSync(PKG_PATH, JSON.stringify(pkg, null, 2) + "\n", "utf-8")
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function bumpVersion(version) {
|
|
21
|
-
const parts = version.split(".").map(Number)
|
|
22
|
-
if (parts.length !== 3 || parts.some(n => Number.isNaN(n) || n < 0)) {
|
|
23
|
-
throw new Error(`Unsupported version format: ${version}`)
|
|
24
|
-
}
|
|
25
|
-
parts[2] += 1
|
|
26
|
-
return parts.join(".")
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async function run(command, args, opts = {}) {
|
|
30
|
-
const result = spawnSync(command, args, { stdio: "inherit", ...opts })
|
|
31
|
-
if (result.error) {
|
|
32
|
-
throw result.error
|
|
33
|
-
}
|
|
34
|
-
if (result.status !== 0) {
|
|
35
|
-
throw new Error(`Command failed: ${command} ${args.join(" ")}`)
|
|
36
|
-
}
|
|
37
|
-
return result
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function verifyCleanWorkingTree() {
|
|
41
|
-
const result = spawnSync("git", ["status", "--porcelain"], {
|
|
42
|
-
encoding: "utf-8",
|
|
43
|
-
stdio: ["ignore", "pipe", "inherit"]
|
|
44
|
-
})
|
|
45
|
-
if (result.error) {
|
|
46
|
-
throw result.error
|
|
47
|
-
}
|
|
48
|
-
if (result.status !== 0) {
|
|
49
|
-
throw new Error("Failed to check git working tree status")
|
|
50
|
-
}
|
|
51
|
-
if (result.stdout.trim()) {
|
|
52
|
-
throw new Error(
|
|
53
|
-
"Git working tree is not clean. Please commit or stash your changes before releasing."
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async function release() {
|
|
59
|
-
verifyCleanWorkingTree()
|
|
60
|
-
const pkg = readPackageJson()
|
|
61
|
-
const currentVersion = pkg.version
|
|
62
|
-
if (!currentVersion) {
|
|
63
|
-
throw new Error("package.json is missing version")
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const arg = process.argv[2]
|
|
67
|
-
const newVersion = arg ? arg.replace(/^v/, "") : bumpVersion(currentVersion)
|
|
68
|
-
const tag = `v${newVersion}`
|
|
69
|
-
|
|
70
|
-
if (newVersion === currentVersion) {
|
|
71
|
-
console.log(`Version unchanged: ${newVersion}`)
|
|
72
|
-
return
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
pkg.version = newVersion
|
|
76
|
-
writePackageJson(pkg)
|
|
77
|
-
|
|
78
|
-
await run("git", ["add", "package.json"])
|
|
79
|
-
await run("git", ["commit", "-m", `release: bump version to ${tag}`])
|
|
80
|
-
await run("git", ["tag", tag])
|
|
81
|
-
await run("git", ["push"])
|
|
82
|
-
await run("git", ["push", "origin", tag])
|
|
83
|
-
|
|
84
|
-
console.log(`Released ${tag}`)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async function main() {
|
|
88
|
-
try {
|
|
89
|
-
await release()
|
|
90
|
-
} catch (error) {
|
|
91
|
-
console.error(error instanceof Error ? error.message : error)
|
|
92
|
-
process.exit(1)
|
|
93
|
-
}
|
|
94
|
-
}
|