screw-up 0.6.1 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -1
- package/dist/index.cjs +52 -7
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -8
- package/dist/internal.d.ts +16 -2
- package/dist/internal.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,6 +48,7 @@ To insert banner header each bundled source files (`dist/index.js` and etc.):
|
|
|
48
48
|
* Flexible output: Specify exactly which keys to include and in what order.
|
|
49
49
|
* Nested object support: Handles nested objects like `author.name`, `repository.url`.
|
|
50
50
|
* Customizable: Choose which metadata fields to include in your banner.
|
|
51
|
+
* TypeScript metadata generation: Automatically generates TypeScript files with metadata constants for use in your source code.
|
|
51
52
|
|
|
52
53
|
## Installation
|
|
53
54
|
|
|
@@ -155,6 +156,66 @@ Results in:
|
|
|
155
156
|
*/
|
|
156
157
|
```
|
|
157
158
|
|
|
159
|
+
### TypeScript Metadata Generation
|
|
160
|
+
|
|
161
|
+
The plugin can generate TypeScript files containing metadata constants that you can import and use in your source code:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { defineConfig } from 'vite';
|
|
165
|
+
import screwUp from 'screw-up';
|
|
166
|
+
|
|
167
|
+
export default defineConfig({
|
|
168
|
+
plugins: [
|
|
169
|
+
screwUp({
|
|
170
|
+
outputMetadataFile: true, // Enable metadata file generation
|
|
171
|
+
outputMetadataFilePath: 'src/generated/packageMetadata.ts', // Custom path (optional)
|
|
172
|
+
outputMetadataKeys: ['name', 'version', 'description', 'author', 'license'] // Keys to include
|
|
173
|
+
})
|
|
174
|
+
],
|
|
175
|
+
build: {
|
|
176
|
+
lib: {
|
|
177
|
+
entry: 'src/index.ts',
|
|
178
|
+
name: 'MyLibrary',
|
|
179
|
+
fileName: 'index'
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
This generates `src/generated/packageMetadata.ts` with sanitized TypeScript constants:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
// This file is auto-generated by screw-up plugin
|
|
189
|
+
// Do not edit manually
|
|
190
|
+
|
|
191
|
+
export const name = "my-awesome-library";
|
|
192
|
+
export const version = "2.1.0";
|
|
193
|
+
export const description = "An awesome TypeScript library";
|
|
194
|
+
export const author = "Jane Developer <jane@example.com>";
|
|
195
|
+
export const license = "Apache-2.0";
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
You can then import and use these constants in your source code:
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { name, version } from './generated/packageMetadata.js';
|
|
202
|
+
|
|
203
|
+
console.log(`${name} v${version}`);
|
|
204
|
+
// Output: my-awesome-library v2.1.0
|
|
205
|
+
|
|
206
|
+
export function getLibraryInfo() {
|
|
207
|
+
return { name, version };
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### Key Sanitization
|
|
212
|
+
|
|
213
|
+
Keys with special characters are automatically sanitized to valid TypeScript identifiers:
|
|
214
|
+
|
|
215
|
+
- `repository.url` → `repository_url`
|
|
216
|
+
- `custom-key` → `custom_key`
|
|
217
|
+
- `123invalid` → `_123invalid`
|
|
218
|
+
|
|
158
219
|
----
|
|
159
220
|
|
|
160
221
|
## Advanced Usage
|
|
@@ -236,4 +297,4 @@ The plugin automatically detects and supports:
|
|
|
236
297
|
|
|
237
298
|
## License
|
|
238
299
|
|
|
239
|
-
Under MIT
|
|
300
|
+
Under MIT
|
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const promises = require("fs/promises");
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const fs = require("fs");
|
|
5
|
-
const flattenObject = (obj, prefix
|
|
5
|
+
const flattenObject = (obj, prefix, map) => {
|
|
6
6
|
for (const [key, value] of Object.entries(obj)) {
|
|
7
7
|
if (!value)
|
|
8
8
|
continue;
|
|
@@ -47,7 +47,7 @@ const findWorkspaceRoot = async (startPath) => {
|
|
|
47
47
|
}
|
|
48
48
|
currentPath = path.dirname(currentPath);
|
|
49
49
|
}
|
|
50
|
-
return
|
|
50
|
+
return void 0;
|
|
51
51
|
};
|
|
52
52
|
const mergePackageMetadata = (parentMetadata, childMetadata) => {
|
|
53
53
|
const merged = {};
|
|
@@ -92,28 +92,73 @@ const generateBanner = (metadata, outputKeys) => {
|
|
|
92
92
|
* ${parts.join("\n * ")}
|
|
93
93
|
*/` : "";
|
|
94
94
|
};
|
|
95
|
+
const insertBannerHeader = (content, banner) => {
|
|
96
|
+
const lines = content.split("\n");
|
|
97
|
+
if (lines.length > 0 && lines[0].startsWith("#!")) {
|
|
98
|
+
return lines[0] + "\n" + banner + "\n" + lines.slice(1).join("\n");
|
|
99
|
+
} else {
|
|
100
|
+
return banner + "\n" + content;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const sanitizeKey = (key) => {
|
|
104
|
+
return key.replace(/[^a-zA-Z0-9_]/g, "_").replace(/^(\d)/, "_$1");
|
|
105
|
+
};
|
|
106
|
+
const generateMetadataFile = (metadata, outputKeys) => {
|
|
107
|
+
const lines = [];
|
|
108
|
+
lines.push("// This file is auto-generated by screw-up plugin");
|
|
109
|
+
lines.push("// Do not edit manually");
|
|
110
|
+
lines.push("");
|
|
111
|
+
for (const key of outputKeys) {
|
|
112
|
+
const value = metadata[key];
|
|
113
|
+
if (value) {
|
|
114
|
+
const sanitizedKey = sanitizeKey(key);
|
|
115
|
+
const escapedValue = JSON.stringify(value);
|
|
116
|
+
lines.push(`export const ${sanitizedKey} = ${escapedValue};`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
lines.push("");
|
|
120
|
+
return lines.join("\n");
|
|
121
|
+
};
|
|
95
122
|
const screwUp = (options = {}) => {
|
|
96
123
|
const {
|
|
97
124
|
outputKeys = ["name", "version", "description", "author", "license", "repository.url"],
|
|
98
|
-
assetFilters = ["\\.d\\.ts$"]
|
|
125
|
+
assetFilters = ["\\.d\\.ts$"],
|
|
126
|
+
outputMetadataFile = false,
|
|
127
|
+
outputMetadataFilePath = "src/generated/packageMetadata.ts",
|
|
128
|
+
outputMetadataKeys = ["name", "version", "description", "author", "license", "repository.url"]
|
|
99
129
|
} = options;
|
|
100
130
|
const assetFiltersRegex = assetFilters.map((filter) => new RegExp(filter));
|
|
101
131
|
let banner;
|
|
132
|
+
let metadata;
|
|
133
|
+
let projectRoot;
|
|
102
134
|
return {
|
|
103
135
|
name: "screw-up",
|
|
104
136
|
apply: "build",
|
|
105
137
|
async configResolved(config) {
|
|
106
|
-
|
|
138
|
+
projectRoot = config.root;
|
|
139
|
+
metadata = await resolvePackageMetadata(config.root);
|
|
107
140
|
banner = generateBanner(metadata, outputKeys);
|
|
108
141
|
},
|
|
142
|
+
async buildStart() {
|
|
143
|
+
if (outputMetadataFile) {
|
|
144
|
+
const metadataContent = generateMetadataFile(metadata, outputMetadataKeys);
|
|
145
|
+
const metadataPath = path.join(projectRoot, outputMetadataFilePath);
|
|
146
|
+
try {
|
|
147
|
+
await promises.mkdir(path.dirname(metadataPath), { recursive: true });
|
|
148
|
+
await promises.writeFile(metadataPath, metadataContent);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.warn(`Failed to write metadata file to ${metadataPath}:`, error);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
},
|
|
109
154
|
generateBundle(_options, bundle) {
|
|
110
155
|
for (const fileName in bundle) {
|
|
111
156
|
const chunk = bundle[fileName];
|
|
112
157
|
if (chunk.type === "chunk") {
|
|
113
|
-
chunk.code =
|
|
158
|
+
chunk.code = insertBannerHeader(chunk.code, banner);
|
|
114
159
|
} else if (chunk.type === "asset" && assetFiltersRegex.some((filter) => filter.test(fileName))) {
|
|
115
160
|
if (typeof chunk.source === "string") {
|
|
116
|
-
chunk.source = banner + "\n
|
|
161
|
+
chunk.source = insertBannerHeader(chunk.source, banner + "\n");
|
|
117
162
|
}
|
|
118
163
|
}
|
|
119
164
|
}
|
|
@@ -128,7 +173,7 @@ const screwUp = (options = {}) => {
|
|
|
128
173
|
try {
|
|
129
174
|
const content = await promises.readFile(filePath, "utf-8");
|
|
130
175
|
if (!content.includes(banner)) {
|
|
131
|
-
await promises.writeFile(filePath, banner + "\n
|
|
176
|
+
await promises.writeFile(filePath, insertBannerHeader(content, banner + "\n"));
|
|
132
177
|
}
|
|
133
178
|
} catch (error) {
|
|
134
179
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,21 @@ export interface ScrewUpOptions {
|
|
|
14
14
|
* @default ['\.d\.ts$']
|
|
15
15
|
*/
|
|
16
16
|
assetFilters?: string[];
|
|
17
|
+
/**
|
|
18
|
+
* Enable TypeScript metadata file generation
|
|
19
|
+
* @default false
|
|
20
|
+
*/
|
|
21
|
+
outputMetadataFile?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Output path for TypeScript metadata file
|
|
24
|
+
* @default 'src/generated/packageMetadata.ts'
|
|
25
|
+
*/
|
|
26
|
+
outputMetadataFilePath?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Array of keys to output in metadata file in the specified order
|
|
29
|
+
* @default ['name', 'version', 'description', 'author', 'license', 'repository.url']
|
|
30
|
+
*/
|
|
31
|
+
outputMetadataKeys?: string[];
|
|
17
32
|
}
|
|
18
33
|
/**
|
|
19
34
|
* Vite plugin that adds banner to the bundled code
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAKnC;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAKnC;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED;;;;GAIG;AACH,QAAA,MAAM,OAAO,GAAI,UAAS,cAAmB,KAAG,MAiF/C,CAAC;AAEF,eAAe,OAAO,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { readFile, readdir, writeFile } from "fs/promises";
|
|
1
|
+
import { readFile, readdir, writeFile, mkdir } from "fs/promises";
|
|
2
2
|
import { join, dirname } from "path";
|
|
3
3
|
import { existsSync } from "fs";
|
|
4
|
-
const flattenObject = (obj, prefix
|
|
4
|
+
const flattenObject = (obj, prefix, map) => {
|
|
5
5
|
for (const [key, value] of Object.entries(obj)) {
|
|
6
6
|
if (!value)
|
|
7
7
|
continue;
|
|
@@ -46,7 +46,7 @@ const findWorkspaceRoot = async (startPath) => {
|
|
|
46
46
|
}
|
|
47
47
|
currentPath = dirname(currentPath);
|
|
48
48
|
}
|
|
49
|
-
return
|
|
49
|
+
return void 0;
|
|
50
50
|
};
|
|
51
51
|
const mergePackageMetadata = (parentMetadata, childMetadata) => {
|
|
52
52
|
const merged = {};
|
|
@@ -91,28 +91,73 @@ const generateBanner = (metadata, outputKeys) => {
|
|
|
91
91
|
* ${parts.join("\n * ")}
|
|
92
92
|
*/` : "";
|
|
93
93
|
};
|
|
94
|
+
const insertBannerHeader = (content, banner) => {
|
|
95
|
+
const lines = content.split("\n");
|
|
96
|
+
if (lines.length > 0 && lines[0].startsWith("#!")) {
|
|
97
|
+
return lines[0] + "\n" + banner + "\n" + lines.slice(1).join("\n");
|
|
98
|
+
} else {
|
|
99
|
+
return banner + "\n" + content;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const sanitizeKey = (key) => {
|
|
103
|
+
return key.replace(/[^a-zA-Z0-9_]/g, "_").replace(/^(\d)/, "_$1");
|
|
104
|
+
};
|
|
105
|
+
const generateMetadataFile = (metadata, outputKeys) => {
|
|
106
|
+
const lines = [];
|
|
107
|
+
lines.push("// This file is auto-generated by screw-up plugin");
|
|
108
|
+
lines.push("// Do not edit manually");
|
|
109
|
+
lines.push("");
|
|
110
|
+
for (const key of outputKeys) {
|
|
111
|
+
const value = metadata[key];
|
|
112
|
+
if (value) {
|
|
113
|
+
const sanitizedKey = sanitizeKey(key);
|
|
114
|
+
const escapedValue = JSON.stringify(value);
|
|
115
|
+
lines.push(`export const ${sanitizedKey} = ${escapedValue};`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
lines.push("");
|
|
119
|
+
return lines.join("\n");
|
|
120
|
+
};
|
|
94
121
|
const screwUp = (options = {}) => {
|
|
95
122
|
const {
|
|
96
123
|
outputKeys = ["name", "version", "description", "author", "license", "repository.url"],
|
|
97
|
-
assetFilters = ["\\.d\\.ts$"]
|
|
124
|
+
assetFilters = ["\\.d\\.ts$"],
|
|
125
|
+
outputMetadataFile = false,
|
|
126
|
+
outputMetadataFilePath = "src/generated/packageMetadata.ts",
|
|
127
|
+
outputMetadataKeys = ["name", "version", "description", "author", "license", "repository.url"]
|
|
98
128
|
} = options;
|
|
99
129
|
const assetFiltersRegex = assetFilters.map((filter) => new RegExp(filter));
|
|
100
130
|
let banner;
|
|
131
|
+
let metadata;
|
|
132
|
+
let projectRoot;
|
|
101
133
|
return {
|
|
102
134
|
name: "screw-up",
|
|
103
135
|
apply: "build",
|
|
104
136
|
async configResolved(config) {
|
|
105
|
-
|
|
137
|
+
projectRoot = config.root;
|
|
138
|
+
metadata = await resolvePackageMetadata(config.root);
|
|
106
139
|
banner = generateBanner(metadata, outputKeys);
|
|
107
140
|
},
|
|
141
|
+
async buildStart() {
|
|
142
|
+
if (outputMetadataFile) {
|
|
143
|
+
const metadataContent = generateMetadataFile(metadata, outputMetadataKeys);
|
|
144
|
+
const metadataPath = join(projectRoot, outputMetadataFilePath);
|
|
145
|
+
try {
|
|
146
|
+
await mkdir(dirname(metadataPath), { recursive: true });
|
|
147
|
+
await writeFile(metadataPath, metadataContent);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.warn(`Failed to write metadata file to ${metadataPath}:`, error);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
},
|
|
108
153
|
generateBundle(_options, bundle) {
|
|
109
154
|
for (const fileName in bundle) {
|
|
110
155
|
const chunk = bundle[fileName];
|
|
111
156
|
if (chunk.type === "chunk") {
|
|
112
|
-
chunk.code =
|
|
157
|
+
chunk.code = insertBannerHeader(chunk.code, banner);
|
|
113
158
|
} else if (chunk.type === "asset" && assetFiltersRegex.some((filter) => filter.test(fileName))) {
|
|
114
159
|
if (typeof chunk.source === "string") {
|
|
115
|
-
chunk.source = banner + "\n
|
|
160
|
+
chunk.source = insertBannerHeader(chunk.source, banner + "\n");
|
|
116
161
|
}
|
|
117
162
|
}
|
|
118
163
|
}
|
|
@@ -127,7 +172,7 @@ const screwUp = (options = {}) => {
|
|
|
127
172
|
try {
|
|
128
173
|
const content = await readFile(filePath, "utf-8");
|
|
129
174
|
if (!content.includes(banner)) {
|
|
130
|
-
await writeFile(filePath, banner + "\n
|
|
175
|
+
await writeFile(filePath, insertBannerHeader(content, banner + "\n"));
|
|
131
176
|
}
|
|
132
177
|
} catch (error) {
|
|
133
178
|
}
|
package/dist/internal.d.ts
CHANGED
|
@@ -8,9 +8,9 @@ export declare const readPackageMetadata: (packagePath: string) => Promise<Packa
|
|
|
8
8
|
/**
|
|
9
9
|
* Find workspace root by looking for workspace configuration files
|
|
10
10
|
* @param startPath - Starting directory path
|
|
11
|
-
* @returns Promise resolving to workspace root path or
|
|
11
|
+
* @returns Promise resolving to workspace root path or undefined if not found
|
|
12
12
|
*/
|
|
13
|
-
export declare const findWorkspaceRoot: (startPath: string) => Promise<string |
|
|
13
|
+
export declare const findWorkspaceRoot: (startPath: string) => Promise<string | undefined>;
|
|
14
14
|
/**
|
|
15
15
|
* Merge package metadata with inheritance (child overrides parent)
|
|
16
16
|
* @param parentMetadata - Parent package metadata
|
|
@@ -31,4 +31,18 @@ export declare const resolvePackageMetadata: (projectRoot: string) => Promise<Pa
|
|
|
31
31
|
* @returns Banner string
|
|
32
32
|
*/
|
|
33
33
|
export declare const generateBanner: (metadata: PackageMetadata, outputKeys: string[]) => string;
|
|
34
|
+
/**
|
|
35
|
+
* Insert banner header at appropriate position considering shebang
|
|
36
|
+
* @param content - The content to insert banner into
|
|
37
|
+
* @param banner - The banner header to insert
|
|
38
|
+
* @returns Content with banner header inserted
|
|
39
|
+
*/
|
|
40
|
+
export declare const insertBannerHeader: (content: string, banner: string) => string;
|
|
41
|
+
/**
|
|
42
|
+
* Generate TypeScript metadata file content from package metadata
|
|
43
|
+
* @param metadata - Package metadata
|
|
44
|
+
* @param outputKeys - Array of keys to output
|
|
45
|
+
* @returns TypeScript file content
|
|
46
|
+
*/
|
|
47
|
+
export declare const generateMetadataFile: (metadata: PackageMetadata, outputKeys: string[]) => string;
|
|
34
48
|
//# sourceMappingURL=internal.d.ts.map
|
package/dist/internal.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AA2BrD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,eAAe,CAWtF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAU,WAAW,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,
|
|
1
|
+
{"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AA2BrD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,eAAe,CAWtF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAU,WAAW,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CA0BrF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAC/B,gBAAgB,eAAe,EAC/B,eAAe,eAAe,KAC7B,eAoBF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,eAAe,CAsBzF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,UAAU,eAAe,EAAE,YAAY,MAAM,EAAE,KAAG,MAWhF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,EAAE,QAAQ,MAAM,KAAG,MAWpE,CAAC;AAYF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,UAAU,eAAe,EAAE,YAAY,MAAM,EAAE,KAAG,MAmBtF,CAAC"}
|