screw-up 0.1.0 → 0.3.0

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
@@ -10,7 +10,7 @@ Simply package metadata inserter for Vite plugins.
10
10
 
11
11
  ## What is this?
12
12
 
13
- This is a Vite plugin that automatically inserts banner comments containing package metadata (name, version, description, author, license) into your bundled JavaScript/CSS files.
13
+ This is a Vite plugin that automatically inserts banner comments containing package metadata (name, version, description, author, license, etc.) into your bundled JavaScript/CSS files.
14
14
 
15
15
  This will automatically read metadata from your `package.json`:
16
16
 
@@ -20,7 +20,10 @@ This will automatically read metadata from your `package.json`:
20
20
  "version": "2.1.0",
21
21
  "description": "An awesome TypeScript library",
22
22
  "author": "Jane Developer <jane@example.com>",
23
- "license": "Apache-2.0"
23
+ "license": "Apache-2.0",
24
+ "repository": {
25
+ "url": "https://github.com/user/my-awesome-library"
26
+ }
24
27
  }
25
28
  ```
26
29
 
@@ -28,17 +31,23 @@ To insert banner header each bundled source files (`dist/index.js` and etc.):
28
31
 
29
32
  ```javascript
30
33
  /*!
31
- * my-awesome-library 2.1.0
32
- * An awesome TypeScript library
33
- * Author: Jane Developer <jane@example.com>
34
- * License: "Apache-2.0
34
+ * name: my-awesome-library
35
+ * version: 2.1.0
36
+ * description: An awesome TypeScript library
37
+ * author: Jane Developer <jane@example.com>
38
+ * license: Apache-2.0
39
+ * repository.url: https://github.com/user/my-awesome-library
35
40
  */
36
41
  // Your bundled code here...
37
42
  ```
38
43
 
39
- * Reads metadata from `package.json`.
40
- * Supports both ESM and CommonJS outputs.
41
- * Customizable banner templates.
44
+ ## Key Features
45
+
46
+ * Automatic metadata extraction: Reads metadata from `package.json` automatically.
47
+ * Workspace support: Works with monorepos and automatically inherits metadata from parent packages.
48
+ * Flexible output: Specify exactly which keys to include and in what order.
49
+ * Nested object support: Handles nested objects like `author.name`, `repository.url`.
50
+ * Customizable: Choose which metadata fields to include in your banner.
42
51
 
43
52
  ## Installation
44
53
 
@@ -56,11 +65,11 @@ Add the plugin to your `vite.config.ts`:
56
65
 
57
66
  ```typescript
58
67
  import { defineConfig } from 'vite';
59
- import { screwUp } from 'screw-up'; // Need to this
68
+ import { screwUp } from 'screw-up';
60
69
 
61
70
  export default defineConfig({
62
71
  plugins: [
63
- screwUp() // Need to this
72
+ screwUp() // Uses default output keys
64
73
  ],
65
74
  build: {
66
75
  lib: {
@@ -72,9 +81,9 @@ export default defineConfig({
72
81
  });
73
82
  ```
74
83
 
75
- ### Custom Banner Template
84
+ ### Custom Output Keys
76
85
 
77
- You can provide a custom banner template:
86
+ You can specify which metadata fields to include and in what order:
78
87
 
79
88
  ```typescript
80
89
  import { defineConfig } from 'vite';
@@ -83,7 +92,7 @@ import { screwUp } from 'screw-up';
83
92
  export default defineConfig({
84
93
  plugins: [
85
94
  screwUp({
86
- bannerTemplate: '/* My Custom Header - Built with ❤️ */'
95
+ outputKeys: ['name', 'version', 'license'] // Only include these fields
87
96
  })
88
97
  ],
89
98
  build: {
@@ -96,79 +105,140 @@ export default defineConfig({
96
105
  });
97
106
  ```
98
107
 
99
- ### Custom Package Path
108
+ This will generate a banner with only the specified fields:
109
+
110
+ ```javascript
111
+ /*!
112
+ * name: my-awesome-library
113
+ * version: 2.1.0
114
+ * license: Apache-2.0
115
+ */
116
+ ```
117
+
118
+ #### Default Output Keys
100
119
 
101
- Specify a different path to your package.json:
120
+ When no `outputKeys` are specified, the plugin uses these default keys:
102
121
 
103
122
  ```typescript
104
- import { defineConfig } from 'vite';
105
- import { screwUp } from 'screw-up';
123
+ ['name', 'version', 'description', 'author', 'license', 'repository.url']
124
+ ```
106
125
 
107
- export default defineConfig({
108
- plugins: [
109
- screwUp({
110
- packagePath: './packages/core/package.json'
111
- })
112
- ]
113
- });
126
+ ### Working with Nested Objects
127
+
128
+ The plugin automatically flattens nested objects using dot notation:
129
+
130
+ ```json
131
+ {
132
+ "name": "my-package",
133
+ "author": {
134
+ "name": "Jane Developer",
135
+ "email": "jane@example.com"
136
+ },
137
+ "repository": {
138
+ "type": "git",
139
+ "url": "https://github.com/user/my-package"
140
+ }
141
+ }
142
+ ```
143
+
144
+ You can reference nested fields in your `outputKeys`:
145
+
146
+ ```typescript
147
+ screwUp({
148
+ outputKeys: ['name', 'author.name', 'author.email', 'repository.url']
149
+ })
114
150
  ```
115
151
 
152
+ Results in:
153
+
154
+ ```javascript
155
+ /*!
156
+ * name: my-package
157
+ * author.name: Jane Developer
158
+ * author.email: jane@example.com
159
+ * repository.url: https://github.com/user/my-package
160
+ */
161
+ ```
162
+
163
+ ----
164
+
116
165
  ## Advanced Usage
117
166
 
118
- ### Working with Monorepos
167
+ ### Monorepo Support
119
168
 
120
- In monorepo setups, you might want to reference a specific package's metadata:
169
+ The plugin automatically detects workspace configurations and inherits metadata from parent packages:
121
170
 
122
- ```typescript
123
- import { defineConfig } from 'vite';
124
- import { screwUp } from 'screw-up';
171
+ ```
172
+ my-monorepo/
173
+ ├── package.json # Root package with shared metadata
174
+ ├── packages/
175
+ │ ├── ui/
176
+ │ │ └── package.json # Child package
177
+ │ └── core/
178
+ │ └── package.json # Child package
179
+ ```
125
180
 
126
- export default defineConfig({
127
- plugins: [
128
- screwUp({
129
- packagePath: '../../packages/ui/package.json'
130
- })
131
- ],
132
- build: {
133
- lib: {
134
- entry: 'src/index.ts',
135
- name: 'UILibrary',
136
- fileName: 'ui'
137
- }
138
- }
139
- });
181
+ Child packages automatically inherit metadata from the root package, with the ability to override specific fields:
182
+
183
+ ```json
184
+ // Root package.json
185
+ {
186
+ "name": "my-monorepo",
187
+ "version": "1.0.0",
188
+ "author": "Company Team",
189
+ "license": "MIT"
190
+ }
191
+
192
+ // packages/ui/package.json
193
+ {
194
+ "name": "@my-monorepo/ui",
195
+ "description": "UI components library"
196
+ }
140
197
  ```
141
198
 
142
- ### Programmatic Banner Generation
199
+ When building the UI package, the banner will include:
200
+
201
+ ```javascript
202
+ /*!
203
+ * name: @my-monorepo/ui
204
+ * version: 1.0.0
205
+ * description: UI components library
206
+ * author: Company Team
207
+ * license: MIT
208
+ */
209
+ ```
210
+
211
+ ### Programmatic Usage
143
212
 
144
213
  You can also use the utility functions directly:
145
214
 
146
215
  ```typescript
147
- import { generateBanner, readPackageMetadata } from 'screw-up';
216
+ import { generateBanner, readPackageMetadata } from 'screw-up/internal';
148
217
 
149
218
  // Read package metadata
150
- const metadata = readPackageMetadata('./package.json');
151
-
152
- // Generate banner
153
- const banner = generateBanner({
154
- name: 'my-package',
155
- version: '1.0.0',
156
- description: 'A great package',
157
- author: 'Developer Name',
158
- license: 'MIT'
159
- });
219
+ const metadata = await readPackageMetadata('./package.json');
220
+
221
+ // Generate banner with custom keys
222
+ const banner = generateBanner(metadata, ['name', 'version', 'license']);
160
223
 
161
224
  console.log(banner);
162
225
  // /*!
163
- // * my-package v1.0.0
164
- // * A great package
165
- // * Author: Developer Name
166
- // * License: MIT
226
+ // * name: my-package
227
+ // * version: 1.0.0
228
+ // * license: MIT
167
229
  // */
168
230
  ```
169
231
 
232
+ ## Supported Workspace Types
233
+
234
+ The plugin automatically detects and supports:
235
+
236
+ - npm/yarn workspaces: Detected via `workspaces` field in `package.json`
237
+ - pnpm workspaces: Detected via `pnpm-workspace.yaml` file
238
+ - Lerna: Detected via `lerna.json` file
239
+
170
240
  ----
171
241
 
172
242
  ## License
173
243
 
174
- Under MIT
244
+ Under MIT
package/dist/index.cjs CHANGED
@@ -1,60 +1,142 @@
1
1
  "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const fs = require("fs");
2
+ const promises = require("fs/promises");
4
3
  const path = require("path");
5
- const generateBanner = (metadata) => {
6
- const name = metadata.name || "Unknown Package";
7
- const version = metadata.version || "0.0.0";
8
- const description = metadata.description || "";
9
- let author = "";
10
- if (metadata.author) {
11
- if (typeof metadata.author === "string") {
12
- author = metadata.author;
4
+ const fs = require("fs");
5
+ const flattenObject = (obj, prefix = "", map) => {
6
+ for (const [key, value] of Object.entries(obj)) {
7
+ if (!value)
8
+ continue;
9
+ const fullKey = prefix ? `${prefix}.${key}` : key;
10
+ if (typeof value === "string") {
11
+ map[fullKey] = value;
12
+ } else if (Array.isArray(value)) {
13
+ map[fullKey] = value.map((v) => String(v)).join(",");
14
+ } else if (typeof value === "object") {
15
+ flattenObject(value, fullKey, map);
13
16
  } else {
14
- author = metadata.author.email ? `${metadata.author.name} <${metadata.author.email}>` : metadata.author.name;
17
+ map[fullKey] = String(value);
15
18
  }
16
19
  }
17
- const license = metadata.license || "";
18
- const parts = [
19
- `${name} ${version}`,
20
- description && `${description}`,
21
- author && `Author: ${author}`,
22
- license && `License: ${license}`
23
- ].filter(Boolean);
24
- return `/*!
25
- * ${parts.join("\n * ")}
26
- */`;
27
20
  };
28
- const readPackageMetadata = (packagePath) => {
21
+ const readPackageMetadata = async (packagePath) => {
29
22
  try {
30
- const content = fs.readFileSync(packagePath, "utf-8");
31
- return JSON.parse(content);
23
+ const content = await promises.readFile(packagePath, "utf-8");
24
+ const json = JSON.parse(content);
25
+ const map = {};
26
+ flattenObject(json, "", map);
27
+ return map;
32
28
  } catch (error) {
33
29
  console.warn(`Failed to read package.json from ${packagePath}:`, error);
34
30
  return {};
35
31
  }
36
32
  };
33
+ const findWorkspaceRoot = async (startPath) => {
34
+ let currentPath = startPath;
35
+ while (currentPath !== path.dirname(currentPath)) {
36
+ const packageJsonPath = path.join(currentPath, "package.json");
37
+ if (fs.existsSync(packageJsonPath)) {
38
+ try {
39
+ const content = await promises.readFile(packageJsonPath, "utf-8");
40
+ const packageJson = JSON.parse(content);
41
+ if (packageJson.workspaces || fs.existsSync(path.join(currentPath, "pnpm-workspace.yaml")) || fs.existsSync(path.join(currentPath, "lerna.json"))) {
42
+ return currentPath;
43
+ }
44
+ } catch (error) {
45
+ console.warn(`Failed to parse package.json at ${packageJsonPath}:`, error);
46
+ }
47
+ }
48
+ currentPath = path.dirname(currentPath);
49
+ }
50
+ return null;
51
+ };
52
+ const mergePackageMetadata = (parentMetadata, childMetadata) => {
53
+ const merged = {};
54
+ for (const key in parentMetadata) {
55
+ const value = parentMetadata[key];
56
+ if (value !== void 0) {
57
+ merged[key] = value;
58
+ }
59
+ }
60
+ for (const key in childMetadata) {
61
+ const value = childMetadata[key];
62
+ if (value !== void 0) {
63
+ merged[key] = value;
64
+ }
65
+ }
66
+ return merged;
67
+ };
68
+ const resolvePackageMetadata = async (projectRoot) => {
69
+ const workspaceRoot = await findWorkspaceRoot(projectRoot);
70
+ if (!workspaceRoot) {
71
+ const localPackagePath = path.join(projectRoot, "package.json");
72
+ return await readPackageMetadata(localPackagePath);
73
+ }
74
+ const projectPackagePath = path.join(projectRoot, "package.json");
75
+ const rootPackagePath = path.join(workspaceRoot, "package.json");
76
+ let metadata = await readPackageMetadata(rootPackagePath);
77
+ if (projectPackagePath !== rootPackagePath && fs.existsSync(projectPackagePath)) {
78
+ const projectMetadata = await readPackageMetadata(projectPackagePath);
79
+ metadata = mergePackageMetadata(metadata, projectMetadata);
80
+ }
81
+ return metadata;
82
+ };
83
+ const generateBanner = (metadata, outputKeys) => {
84
+ const parts = [];
85
+ for (const key of outputKeys) {
86
+ const value = metadata[key];
87
+ if (value) {
88
+ parts.push(`${key}: ${value}`);
89
+ }
90
+ }
91
+ return parts.length > 0 ? `/*!
92
+ * ${parts.join("\n * ")}
93
+ */` : "";
94
+ };
37
95
  const screwUp = (options = {}) => {
38
- const { packagePath = "./package.json", bannerTemplate } = options;
96
+ const {
97
+ outputKeys = ["name", "version", "description", "author", "license", "repository.url"],
98
+ assetFilters = ["\\.d\\.ts$"]
99
+ } = options;
100
+ const assetFiltersRegex = assetFilters.map((filter) => new RegExp(filter));
39
101
  let banner;
40
102
  return {
41
103
  name: "screw-up",
42
104
  apply: "build",
43
- configResolved(config) {
44
- const resolvedPackagePath = path.resolve(config.root, packagePath);
45
- const metadata = readPackageMetadata(resolvedPackagePath);
46
- banner = bannerTemplate || generateBanner(metadata);
105
+ async configResolved(config) {
106
+ const metadata = await resolvePackageMetadata(config.root);
107
+ banner = generateBanner(metadata, outputKeys);
47
108
  },
48
109
  generateBundle(_options, bundle) {
49
110
  for (const fileName in bundle) {
50
111
  const chunk = bundle[fileName];
51
112
  if (chunk.type === "chunk") {
52
113
  chunk.code = banner + "\n" + chunk.code;
114
+ } else if (chunk.type === "asset" && assetFiltersRegex.some((filter) => filter.test(fileName))) {
115
+ if (typeof chunk.source === "string") {
116
+ chunk.source = banner + "\n\n" + chunk.source;
117
+ }
118
+ }
119
+ }
120
+ },
121
+ async writeBundle(options2) {
122
+ if (!options2.dir) return;
123
+ try {
124
+ const files = await promises.readdir(options2.dir, { recursive: true });
125
+ for (const file of files) {
126
+ const filePath = path.join(options2.dir, file);
127
+ if (assetFiltersRegex.some((filter) => filter.test(file))) {
128
+ try {
129
+ const content = await promises.readFile(filePath, "utf-8");
130
+ if (!content.includes(banner)) {
131
+ await promises.writeFile(filePath, banner + "\n\n" + content);
132
+ }
133
+ } catch (error) {
134
+ }
135
+ }
53
136
  }
137
+ } catch (error) {
54
138
  }
55
139
  }
56
140
  };
57
141
  };
58
- exports.generateBanner = generateBanner;
59
- exports.readPackageMetadata = readPackageMetadata;
60
- exports.screwUp = screwUp;
142
+ module.exports = screwUp;
package/dist/index.d.ts CHANGED
@@ -1,44 +1,22 @@
1
1
  import { Plugin } from 'vite';
2
2
 
3
- interface PackageMetadata {
4
- name?: string;
5
- version?: string;
6
- description?: string;
7
- author?: string | {
8
- name: string;
9
- email?: string;
10
- };
11
- license?: string;
12
- }
13
- /**
14
- * Generate banner string from package.json metadata
15
- * @param metadata - Package metadata
16
- * @returns Banner string
17
- */
18
- export declare const generateBanner: (metadata: PackageMetadata) => string;
19
- /**
20
- * Read and parse package.json file
21
- * @param packagePath - Path to package.json
22
- * @returns Package metadata
23
- */
24
- export declare const readPackageMetadata: (packagePath: string) => PackageMetadata;
25
3
  export interface ScrewUpOptions {
26
4
  /**
27
- * Path to package.json file
28
- * @default "./package.json"
5
+ * Array of keys to output in banner in the specified order
6
+ * @default ['name', 'version', 'description', 'author', 'license', 'repository.url']
29
7
  */
30
- packagePath?: string;
8
+ outputKeys?: string[];
31
9
  /**
32
- * Custom banner template
33
- * @default undefined (uses built-in template)
10
+ * Array of asset file regex to add banner to
11
+ * @default ['\.d\.ts$']
34
12
  */
35
- bannerTemplate?: string;
13
+ assetFilters?: string[];
36
14
  }
37
15
  /**
38
16
  * Vite plugin that adds banner to the bundled code
39
17
  * @param options - Plugin options
40
18
  * @returns Vite plugin
41
19
  */
42
- export declare const screwUp: (options?: ScrewUpOptions) => Plugin;
43
- export {};
20
+ declare const screwUp: (options?: ScrewUpOptions) => Plugin;
21
+ export default screwUp;
44
22
  //# sourceMappingURL=index.d.ts.map
@@ -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;AAInC,UAAU,eAAe;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,UAAU,eAAe,KAAG,MA0B1D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAAI,aAAa,MAAM,KAAG,eAQzD,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,eAAO,MAAM,OAAO,GAAI,UAAS,cAAmB,KAAG,MAsBtD,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,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"}
package/dist/index.js CHANGED
@@ -1,60 +1,143 @@
1
- import { readFileSync } from "fs";
2
- import { resolve } from "path";
3
- const generateBanner = (metadata) => {
4
- const name = metadata.name || "Unknown Package";
5
- const version = metadata.version || "0.0.0";
6
- const description = metadata.description || "";
7
- let author = "";
8
- if (metadata.author) {
9
- if (typeof metadata.author === "string") {
10
- author = metadata.author;
1
+ import { readFile, readdir, writeFile } from "fs/promises";
2
+ import { join, dirname } from "path";
3
+ import { existsSync } from "fs";
4
+ const flattenObject = (obj, prefix = "", map) => {
5
+ for (const [key, value] of Object.entries(obj)) {
6
+ if (!value)
7
+ continue;
8
+ const fullKey = prefix ? `${prefix}.${key}` : key;
9
+ if (typeof value === "string") {
10
+ map[fullKey] = value;
11
+ } else if (Array.isArray(value)) {
12
+ map[fullKey] = value.map((v) => String(v)).join(",");
13
+ } else if (typeof value === "object") {
14
+ flattenObject(value, fullKey, map);
11
15
  } else {
12
- author = metadata.author.email ? `${metadata.author.name} <${metadata.author.email}>` : metadata.author.name;
16
+ map[fullKey] = String(value);
13
17
  }
14
18
  }
15
- const license = metadata.license || "";
16
- const parts = [
17
- `${name} ${version}`,
18
- description && `${description}`,
19
- author && `Author: ${author}`,
20
- license && `License: ${license}`
21
- ].filter(Boolean);
22
- return `/*!
23
- * ${parts.join("\n * ")}
24
- */`;
25
19
  };
26
- const readPackageMetadata = (packagePath) => {
20
+ const readPackageMetadata = async (packagePath) => {
27
21
  try {
28
- const content = readFileSync(packagePath, "utf-8");
29
- return JSON.parse(content);
22
+ const content = await readFile(packagePath, "utf-8");
23
+ const json = JSON.parse(content);
24
+ const map = {};
25
+ flattenObject(json, "", map);
26
+ return map;
30
27
  } catch (error) {
31
28
  console.warn(`Failed to read package.json from ${packagePath}:`, error);
32
29
  return {};
33
30
  }
34
31
  };
32
+ const findWorkspaceRoot = async (startPath) => {
33
+ let currentPath = startPath;
34
+ while (currentPath !== dirname(currentPath)) {
35
+ const packageJsonPath = join(currentPath, "package.json");
36
+ if (existsSync(packageJsonPath)) {
37
+ try {
38
+ const content = await readFile(packageJsonPath, "utf-8");
39
+ const packageJson = JSON.parse(content);
40
+ if (packageJson.workspaces || existsSync(join(currentPath, "pnpm-workspace.yaml")) || existsSync(join(currentPath, "lerna.json"))) {
41
+ return currentPath;
42
+ }
43
+ } catch (error) {
44
+ console.warn(`Failed to parse package.json at ${packageJsonPath}:`, error);
45
+ }
46
+ }
47
+ currentPath = dirname(currentPath);
48
+ }
49
+ return null;
50
+ };
51
+ const mergePackageMetadata = (parentMetadata, childMetadata) => {
52
+ const merged = {};
53
+ for (const key in parentMetadata) {
54
+ const value = parentMetadata[key];
55
+ if (value !== void 0) {
56
+ merged[key] = value;
57
+ }
58
+ }
59
+ for (const key in childMetadata) {
60
+ const value = childMetadata[key];
61
+ if (value !== void 0) {
62
+ merged[key] = value;
63
+ }
64
+ }
65
+ return merged;
66
+ };
67
+ const resolvePackageMetadata = async (projectRoot) => {
68
+ const workspaceRoot = await findWorkspaceRoot(projectRoot);
69
+ if (!workspaceRoot) {
70
+ const localPackagePath = join(projectRoot, "package.json");
71
+ return await readPackageMetadata(localPackagePath);
72
+ }
73
+ const projectPackagePath = join(projectRoot, "package.json");
74
+ const rootPackagePath = join(workspaceRoot, "package.json");
75
+ let metadata = await readPackageMetadata(rootPackagePath);
76
+ if (projectPackagePath !== rootPackagePath && existsSync(projectPackagePath)) {
77
+ const projectMetadata = await readPackageMetadata(projectPackagePath);
78
+ metadata = mergePackageMetadata(metadata, projectMetadata);
79
+ }
80
+ return metadata;
81
+ };
82
+ const generateBanner = (metadata, outputKeys) => {
83
+ const parts = [];
84
+ for (const key of outputKeys) {
85
+ const value = metadata[key];
86
+ if (value) {
87
+ parts.push(`${key}: ${value}`);
88
+ }
89
+ }
90
+ return parts.length > 0 ? `/*!
91
+ * ${parts.join("\n * ")}
92
+ */` : "";
93
+ };
35
94
  const screwUp = (options = {}) => {
36
- const { packagePath = "./package.json", bannerTemplate } = options;
95
+ const {
96
+ outputKeys = ["name", "version", "description", "author", "license", "repository.url"],
97
+ assetFilters = ["\\.d\\.ts$"]
98
+ } = options;
99
+ const assetFiltersRegex = assetFilters.map((filter) => new RegExp(filter));
37
100
  let banner;
38
101
  return {
39
102
  name: "screw-up",
40
103
  apply: "build",
41
- configResolved(config) {
42
- const resolvedPackagePath = resolve(config.root, packagePath);
43
- const metadata = readPackageMetadata(resolvedPackagePath);
44
- banner = bannerTemplate || generateBanner(metadata);
104
+ async configResolved(config) {
105
+ const metadata = await resolvePackageMetadata(config.root);
106
+ banner = generateBanner(metadata, outputKeys);
45
107
  },
46
108
  generateBundle(_options, bundle) {
47
109
  for (const fileName in bundle) {
48
110
  const chunk = bundle[fileName];
49
111
  if (chunk.type === "chunk") {
50
112
  chunk.code = banner + "\n" + chunk.code;
113
+ } else if (chunk.type === "asset" && assetFiltersRegex.some((filter) => filter.test(fileName))) {
114
+ if (typeof chunk.source === "string") {
115
+ chunk.source = banner + "\n\n" + chunk.source;
116
+ }
117
+ }
118
+ }
119
+ },
120
+ async writeBundle(options2) {
121
+ if (!options2.dir) return;
122
+ try {
123
+ const files = await readdir(options2.dir, { recursive: true });
124
+ for (const file of files) {
125
+ const filePath = join(options2.dir, file);
126
+ if (assetFiltersRegex.some((filter) => filter.test(file))) {
127
+ try {
128
+ const content = await readFile(filePath, "utf-8");
129
+ if (!content.includes(banner)) {
130
+ await writeFile(filePath, banner + "\n\n" + content);
131
+ }
132
+ } catch (error) {
133
+ }
134
+ }
51
135
  }
136
+ } catch (error) {
52
137
  }
53
138
  }
54
139
  };
55
140
  };
56
141
  export {
57
- generateBanner,
58
- readPackageMetadata,
59
- screwUp
142
+ screwUp as default
60
143
  };
@@ -0,0 +1,34 @@
1
+ export type PackageMetadata = Record<string, string>;
2
+ /**
3
+ * Read and parse package.json file
4
+ * @param packagePath - Path to package.json
5
+ * @returns Promise resolving to package metadata
6
+ */
7
+ export declare const readPackageMetadata: (packagePath: string) => Promise<PackageMetadata>;
8
+ /**
9
+ * Find workspace root by looking for workspace configuration files
10
+ * @param startPath - Starting directory path
11
+ * @returns Promise resolving to workspace root path or null if not found
12
+ */
13
+ export declare const findWorkspaceRoot: (startPath: string) => Promise<string | null>;
14
+ /**
15
+ * Merge package metadata with inheritance (child overrides parent)
16
+ * @param parentMetadata - Parent package metadata
17
+ * @param childMetadata - Child package metadata
18
+ * @returns Merged metadata
19
+ */
20
+ export declare const mergePackageMetadata: (parentMetadata: PackageMetadata, childMetadata: PackageMetadata) => PackageMetadata;
21
+ /**
22
+ * Resolve package metadata for current project with workspace inheritance
23
+ * @param projectRoot - Current project root
24
+ * @returns Promise resolving to resolved package metadata
25
+ */
26
+ export declare const resolvePackageMetadata: (projectRoot: string) => Promise<PackageMetadata>;
27
+ /**
28
+ * Generate banner string from package.json metadata
29
+ * @param metadata - Package metadata
30
+ * @param outputKeys - Array of keys to output in specified order
31
+ * @returns Banner string
32
+ */
33
+ export declare const generateBanner: (metadata: PackageMetadata, outputKeys: string[]) => string;
34
+ //# sourceMappingURL=internal.d.ts.map
@@ -0,0 +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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "screw-up",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Simply package metadata inserter on Vite plugin",
5
5
  "keywords": [
6
6
  "vite",