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 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 = "", map) => {
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 null;
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
- const metadata = await resolvePackageMetadata(config.root);
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 = banner + "\n" + 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\n" + chunk.source;
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\n" + content);
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
@@ -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;CACzB;AAED;;;;GAIG;AACH,QAAA,MAAM,OAAO,GAAI,UAAS,cAAmB,KAAG,MA2D/C,CAAC;AAEF,eAAe,OAAO,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 = "", map) => {
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 null;
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
- const metadata = await resolvePackageMetadata(config.root);
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 = banner + "\n" + 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\n" + chunk.source;
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\n" + content);
175
+ await writeFile(filePath, insertBannerHeader(content, banner + "\n"));
131
176
  }
132
177
  } catch (error) {
133
178
  }
@@ -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 null if not found
11
+ * @returns Promise resolving to workspace root path or undefined if not found
12
12
  */
13
- export declare const findWorkspaceRoot: (startPath: string) => Promise<string | null>;
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
@@ -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,IAAI,CA0BhF,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"}
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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "screw-up",
3
- "version": "0.6.1",
3
+ "version": "0.8.1",
4
4
  "description": "Simply package metadata inserter on Vite plugin",
5
5
  "keywords": [
6
6
  "vite",