rollup-plugin-replace-shebang 1.2.0 → 2.0.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
@@ -1,46 +1,144 @@
1
- <div style="text-align: center;" align="center">
2
-
3
1
  # rollup-plugin-replace-shebang
4
2
 
5
- 一个自动替换`shebang`的`rollup`插件
6
-
7
3
  [![NPM version][npm-image]][npm-url]
8
- [![Codacy Badge][codacy-image]][codacy-url]
9
- [![Test coverage][codecov-image]][codecov-url]
10
4
  [![npm download][download-image]][download-url]
11
- [![gzip][gzip-image]][gzip-url]
12
5
  [![License][license-image]][license-url]
13
6
 
14
- [![Sonar][sonar-image]][sonar-url]
7
+ A Rollup plugin that preserves and relocates shebang (`#!`) to the output bundle.
8
+
9
+ [简体中文](./README_CN.md) | [Documentation](https://saqqdy.github.io/rollup-plugin-replace-shebang/)
10
+
11
+ ## Background
12
+
13
+ When building CLI tools with Rollup, the shebang (`#!/usr/bin/env node`) at the top of your entry file gets removed during bundling. This plugin ensures your shebang is preserved in the final output.
15
14
 
16
- </div>
15
+ ## Online Examples
17
16
 
18
- ## 安装
17
+ Try the plugin online with StackBlitz:
18
+
19
+ | Example | Description | Link |
20
+ |---------|-------------|------|
21
+ | rollup-v2 | Rollup 2.x with template variables | [Open in StackBlitz](https://stackblitz.com/github/saqqdy/rollup-plugin-replace-shebang/tree/master/examples/rollup-v2) |
22
+ | rollup-v4 | Rollup 4.x multi-file project | [Open in StackBlitz](https://stackblitz.com/github/saqqdy/rollup-plugin-replace-shebang/tree/master/examples/rollup-v4) |
23
+
24
+ ## Installation
19
25
 
20
26
  ```bash
21
- # 使用npm
22
- $ npm install -D rollup-plugin-replace-shebang
27
+ # pnpm
28
+ pnpm add -D rollup-plugin-replace-shebang
23
29
 
24
- # 使用yarn
25
- $ yarn add -D rollup-plugin-replace-shebang
30
+ # npm
31
+ npm install -D rollup-plugin-replace-shebang
26
32
  ```
27
33
 
28
- ## 使用
34
+ ## Usage
29
35
 
30
36
  ```js
31
- import shebang from 'rollup-plugin-replace-shebang'
32
-
33
- plugins: [
34
- shebang({
35
- shebang: '#!/usr/bin/env node',
36
- skipBackslash: true // 跳过\u005c 反斜杠
37
+ import replaceShebang from 'rollup-plugin-replace-shebang'
38
+
39
+ export default {
40
+ input: 'src/cli.js',
41
+ output: {
42
+ file: 'dist/cli.js',
43
+ format: 'es'
44
+ },
45
+ plugins: [
46
+ replaceShebang({
47
+ shebang: '#!/usr/bin/env node',
48
+ skipBackslash: true,
49
+ chmod: true
37
50
  })
38
- ]
51
+ ]
52
+ }
53
+ ```
54
+
55
+ ## Options
56
+
57
+ | Option | Type | Default | Description |
58
+ |--------|------|---------|-------------|
59
+ | `shebang` | `string` | Original shebang | Custom shebang to prepend. Supports template variables: `${name}`, `${version}` |
60
+ | `skipBackslash` | `boolean` | `false` | Preserve `\u005c` escape sequences |
61
+ | `preserve` | `boolean` | `false` | Preserve original shebang without modification |
62
+ | `chmod` | `boolean` | `false` | Auto set executable permission (chmod +x) on output files |
63
+ | `include` | `string \| string[]` | `['**/*.js', '**/*.ts', ...]` | Files to include |
64
+ | `exclude` | `string \| string[]` | `['node_modules/**']` | Files to exclude |
65
+ | `warnOnMultiple` | `boolean` | `true` | Warn when multiple files have shebangs |
66
+
67
+ ### Template Variables
68
+
69
+ The `shebang` option supports template variables:
70
+
71
+ ```js
72
+ replaceShebang({
73
+ shebang: '#!/usr/bin/env node # ${name} v${version}'
74
+ })
75
+ // Output: #!/usr/bin/env node # rollup-plugin-replace-shebang v2.0.0
76
+ ```
77
+
78
+ ### Include/Exclude Patterns
79
+
80
+ The plugin supports various glob patterns for filtering files:
81
+
82
+ | Pattern | Example | Matches |
83
+ |---------|---------|---------|
84
+ | Exact match | `src/cli.ts` | Only `src/cli.ts` |
85
+ | Extension match | `**/*.ts` | Any `.ts` file in any directory |
86
+ | Prefix match | `src/**` | Any file under `src/` |
87
+ | Directory match | `**/node_modules/**` | Any file in `node_modules` anywhere |
88
+ | Wildcard match | `src/*.test.ts` | `src/foo.test.ts` but not `src/sub/foo.test.ts` |
89
+ | Plain string | `node_modules` | Any path containing `node_modules` |
90
+
91
+ ```js
92
+ replaceShebang({
93
+ include: ['src/cli.ts', 'src/bin/*.ts'],
94
+ exclude: ['node_modules/**', '**/*.test.ts']
95
+ })
96
+ ```
97
+
98
+ ## How it works
99
+
100
+ 1. **Transform phase**: Extracts and removes shebang from source files
101
+ 2. **RenderChunk phase**: Prepends shebang to the output bundle
102
+ 3. **WriteBundle phase**: Optionally sets executable permissions
103
+
104
+ ## Plugin API
105
+
106
+ The plugin exposes an API for external access:
107
+
108
+ ```js
109
+ const plugin = replaceShebang()
110
+
111
+ // Get shebang for a specific file
112
+ plugin.api.getShebang('/path/to/file.js')
113
+
114
+ // Get all shebangs
115
+ plugin.api.getAllShebangs()
39
116
  ```
40
117
 
41
- ## 问题和支持
118
+ ## Example
119
+
120
+ **Input (src/cli.js):**
121
+ ```js
122
+ #!/usr/bin/env node
123
+ import { program } from 'commander'
124
+
125
+ program.parse()
126
+ ```
127
+
128
+ **Output (dist/cli.js):**
129
+ ```js
130
+ #!/usr/bin/env node
131
+ // bundled code...
132
+ ```
133
+
134
+ ## Requirements
135
+
136
+ - Rollup >= 2.0.0
137
+ - Node.js >= 12
138
+
139
+ ## Feedback
42
140
 
43
- Please open an issue [here](https://github.com/saqqdy/rollup-plugin-replace-shebang/issues).
141
+ Feel free to submit an [Issue](https://github.com/saqqdy/rollup-plugin-replace-shebang/issues) for bugs or suggestions.
44
142
 
45
143
  ## License
46
144
 
@@ -48,15 +146,7 @@ Please open an issue [here](https://github.com/saqqdy/rollup-plugin-replace-sheb
48
146
 
49
147
  [npm-image]: https://img.shields.io/npm/v/rollup-plugin-replace-shebang.svg?style=flat-square
50
148
  [npm-url]: https://npmjs.org/package/rollup-plugin-replace-shebang
51
- [codacy-image]: https://app.codacy.com/project/badge/Grade/f70d4880e4ad4f40aa970eb9ee9d0696
52
- [codacy-url]: https://www.codacy.com/gh/saqqdy/rollup-plugin-replace-shebang/dashboard?utm_source=github.com&utm_medium=referral&utm_content=saqqdy/rollup-plugin-replace-shebang&utm_campaign=Badge_Grade
53
- [codecov-image]: https://img.shields.io/codecov/c/github/saqqdy/rollup-plugin-replace-shebang.svg?style=flat-square
54
- [codecov-url]: https://codecov.io/github/saqqdy/rollup-plugin-replace-shebang?branch=master
55
149
  [download-image]: https://img.shields.io/npm/dm/rollup-plugin-replace-shebang.svg?style=flat-square
56
150
  [download-url]: https://npmjs.org/package/rollup-plugin-replace-shebang
57
- [gzip-image]: http://img.badgesize.io/https://unpkg.com/rollup-plugin-replace-shebang/lib/index.js?compression=gzip&label=gzip%20size:%20JS
58
- [gzip-url]: http://img.badgesize.io/https://unpkg.com/rollup-plugin-replace-shebang/lib/index.js?compression=gzip&label=gzip%20size:%20JS
59
151
  [license-image]: https://img.shields.io/badge/License-MIT-yellow.svg
60
152
  [license-url]: LICENSE
61
- [sonar-image]: https://sonarcloud.io/api/project_badges/quality_gate?project=saqqdy_rollup-plugin-replace-shebang
62
- [sonar-url]: https://sonarcloud.io/dashboard?id=saqqdy_rollup-plugin-replace-shebang
package/dist/index.cjs ADDED
@@ -0,0 +1,202 @@
1
+ /*!
2
+ * rollup-plugin-replace-shebang v2.0.0
3
+ * (c) 2021-present saqqdy <https://github.com/saqqdy>
4
+ * Released under the MIT License.
5
+ */
6
+ //#region \0rolldown/runtime.js
7
+ var __create = Object.create;
8
+ var __defProp = Object.defineProperty;
9
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
10
+ var __getOwnPropNames = Object.getOwnPropertyNames;
11
+ var __getProtoOf = Object.getPrototypeOf;
12
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
16
+ key = keys[i];
17
+ if (!__hasOwnProp.call(to, key) && key !== except) {
18
+ __defProp(to, key, {
19
+ get: ((k) => from[k]).bind(null, key),
20
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
21
+ });
22
+ }
23
+ }
24
+ }
25
+ return to;
26
+ };
27
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
28
+ value: mod,
29
+ enumerable: true
30
+ }) : target, mod));
31
+
32
+ //#endregion
33
+ let magic_string = require("magic-string");
34
+ magic_string = __toESM(magic_string);
35
+
36
+ //#region src/index.ts
37
+ const PACKAGE_NAME = "rollup-plugin-replace-shebang";
38
+ const PACKAGE_VERSION = "2.0.0";
39
+ /**
40
+ * Validates if a string is a valid shebang.
41
+ * Valid shebangs start with #! followed by a path.
42
+ */
43
+ function validateShebang(shebang) {
44
+ return /^#!\/[^\s]*\S/.test(shebang);
45
+ }
46
+ /**
47
+ * Creates a filter function from include/exclude patterns.
48
+ */
49
+ function createFilter(include, exclude) {
50
+ if (!include && !exclude) return (id) => !id.includes("node_modules");
51
+ const includePatterns = include ? Array.isArray(include) ? include : [include] : null;
52
+ const excludePatterns = exclude ? Array.isArray(exclude) ? exclude : [exclude] : null;
53
+ /**
54
+ * Check if a pattern matches the given id
55
+ */
56
+ function matchPattern(pattern, id) {
57
+ if (id === pattern) return true;
58
+ if (pattern.startsWith("**/*.") && id.endsWith(pattern.slice(4))) return true;
59
+ if (pattern.endsWith("/**") && id.startsWith(pattern.slice(0, -3))) return true;
60
+ if (pattern.startsWith("**/") && pattern.endsWith("/**")) {
61
+ const segment = pattern.slice(3, -3);
62
+ return id.includes(`/${segment}/`) || id.startsWith(`${segment}/`);
63
+ }
64
+ if (pattern.includes("*")) return patternToRegex(pattern).test(id);
65
+ return id.includes(`/${pattern}/`) || id.includes(pattern);
66
+ }
67
+ return (id) => {
68
+ if (excludePatterns) {
69
+ for (const pattern of excludePatterns) if (matchPattern(pattern, id)) return false;
70
+ }
71
+ if (includePatterns) {
72
+ for (const pattern of includePatterns) if (matchPattern(pattern, id)) return true;
73
+ return false;
74
+ }
75
+ return true;
76
+ };
77
+ }
78
+ /**
79
+ * Converts a glob pattern to RegExp.
80
+ */
81
+ function patternToRegex(pattern) {
82
+ const regex = pattern.replace(/\*\*\//g, "<<DOUBLE_STAR_SLASH>>").replace(/\*\*/g, "<<DOUBLE_STAR>>").replace(/\*/g, "[^/]*").replace(/<<DOUBLE_STAR_SLASH>>/g, "(.*\\/)?").replace(/<<DOUBLE_STAR>>/g, ".*").replace(/\?/g, "[^/]").replace(/\./g, "\\.").replace(/\//g, "\\/");
83
+ return new RegExp(`^${regex}$`);
84
+ }
85
+ /**
86
+ * Resolves template variables in shebang string.
87
+ */
88
+ function resolveTemplateVars(shebang, vars) {
89
+ return shebang.replace(/\$\{(\w+)\}/g, (_, key) => vars[key] || "");
90
+ }
91
+ /**
92
+ * A Rollup plugin that preserves and relocates shebang (#!) to the output bundle.
93
+ *
94
+ * During the build process, shebangs are removed from individual modules and
95
+ * re-added to the final bundle, ensuring CLI tools work correctly.
96
+ *
97
+ * @param options - Plugin configuration options
98
+ * @returns A Rollup plugin instance
99
+ *
100
+ * @example
101
+ * ```js
102
+ * import replaceShebang from 'rollup-plugin-replace-shebang'
103
+ *
104
+ * export default {
105
+ * plugins: [
106
+ * replaceShebang({
107
+ * shebang: '#!/usr/bin/env node',
108
+ * skipBackslash: true,
109
+ * chmod: true,
110
+ * include: ['src/cli.ts'],
111
+ * exclude: ['node_modules/**']
112
+ * })
113
+ * ]
114
+ * }
115
+ * ```
116
+ */
117
+ function replaceShebangPlugin(options = {}) {
118
+ const contextMap = /* @__PURE__ */ new Map();
119
+ const filter = createFilter(options.include, options.exclude);
120
+ let hasWarnedMultiple = false;
121
+ const transform = (code, moduleID) => {
122
+ if (!filter(moduleID)) return null;
123
+ if (options.preserve) return null;
124
+ let shebang, newCode = code.replace(/^#![^\n]*/, (match) => {
125
+ shebang = match;
126
+ return "";
127
+ });
128
+ if (options.skipBackslash) newCode = newCode.replace(/(\\u005c|\\\\)/g, () => "__u005c__");
129
+ if (!shebang) return null;
130
+ if (!validateShebang(shebang)) console.warn(`[${PACKAGE_NAME}] Invalid shebang format: "${shebang}" in ${moduleID}`);
131
+ const normalizedID = moduleID.replace(/\?.+$/, "");
132
+ contextMap.set(normalizedID, {
133
+ shebang,
134
+ id: normalizedID
135
+ });
136
+ if (options.warnOnMultiple !== false && contextMap.size > 1 && !hasWarnedMultiple) {
137
+ console.warn(`[${PACKAGE_NAME}] Multiple files with shebang detected. Only the entry file's shebang will be used in output.`);
138
+ hasWarnedMultiple = true;
139
+ }
140
+ return {
141
+ code: newCode,
142
+ map: null
143
+ };
144
+ };
145
+ const renderChunk = (code, chunk, { sourcemap }) => {
146
+ var _chunk$facadeModuleId, _options$shebang;
147
+ const moduleID = (_chunk$facadeModuleId = chunk.facadeModuleId) === null || _chunk$facadeModuleId === void 0 ? void 0 : _chunk$facadeModuleId.replace(/\?.+$/, "");
148
+ if (!moduleID) return null;
149
+ const info = contextMap.get(moduleID);
150
+ if (!info) return null;
151
+ let finalCode = code, resolvedShebang = (_options$shebang = options.shebang) !== null && _options$shebang !== void 0 ? _options$shebang : info.shebang;
152
+ if (options.skipBackslash) finalCode = finalCode.replace(/__u005c__/g, () => "\\u005c");
153
+ resolvedShebang = resolveTemplateVars(resolvedShebang, {
154
+ name: PACKAGE_NAME,
155
+ version: PACKAGE_VERSION
156
+ });
157
+ const ms = new magic_string.default(finalCode);
158
+ ms.prepend(`${resolvedShebang}\n`);
159
+ return {
160
+ code: ms.toString(),
161
+ map: sourcemap ? ms.generateMap({ hires: true }) : null
162
+ };
163
+ };
164
+ const buildEnd = () => {};
165
+ const writeBundle = async (outputOptions, bundle) => {
166
+ contextMap.clear();
167
+ if (options.chmod) {
168
+ for (const fileName of Object.keys(bundle)) if (fileName.endsWith(".js") || fileName.endsWith(".mjs") || fileName.endsWith(".cjs")) {
169
+ const chunk = bundle[fileName];
170
+ if (chunk && "code" in chunk) {
171
+ if (chunk.code.startsWith("#!")) {
172
+ const fs = await import("node:fs/promises");
173
+ const path = await import("node:path");
174
+ const outputPath = outputOptions.file ? path.resolve(outputOptions.file) : path.join(outputOptions.dir || "dist", fileName);
175
+ try {
176
+ await fs.chmod(outputPath, 493);
177
+ } catch {}
178
+ }
179
+ }
180
+ }
181
+ }
182
+ };
183
+ return {
184
+ name: "replace-shebang",
185
+ version: PACKAGE_VERSION,
186
+ transform,
187
+ renderChunk,
188
+ buildEnd,
189
+ writeBundle,
190
+ api: {
191
+ getShebang: (id) => {
192
+ var _contextMap$get;
193
+ return (_contextMap$get = contextMap.get(id)) === null || _contextMap$get === void 0 ? void 0 : _contextMap$get.shebang;
194
+ },
195
+ getAllShebangs: () => new Map(contextMap)
196
+ }
197
+ };
198
+ }
199
+
200
+ //#endregion
201
+ module.exports = replaceShebangPlugin;
202
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["MagicString"],"sources":["../src/index.ts"],"sourcesContent":["import type { Plugin, RenderChunkHook, TransformHook } from 'rollup'\nimport MagicString from 'magic-string'\n\nexport interface Options {\n\t/**\n\t * Custom shebang to prepend to the output.\n\t * Defaults to the original shebang from the source file.\n\t * Supports template variables: ${name}, ${version}\n\t */\n\tshebang?: string\n\t/**\n\t * Whether to skip backslash escape processing.\n\t * When true, preserves `\\u005c` escape sequences.\n\t * @default false\n\t */\n\tskipBackslash?: boolean\n\t/**\n\t * Preserve the original shebang without modification.\n\t * @default false\n\t */\n\tpreserve?: boolean\n\t/**\n\t * Automatically set executable permission (chmod +x) on output files.\n\t * Only works when output is a file, not stdout.\n\t * @default false\n\t */\n\tchmod?: boolean\n\t/**\n\t * Include pattern for files to process.\n\t * @default [\"**\\/*.js\", \"**\\/*.mjs\", \"**\\/*.ts\", \"**\\/*.mts\", \"**\\/*.cjs\", \"**\\/*.cts\"]\n\t */\n\tinclude?: string | string[]\n\t/**\n\t * Exclude pattern for files to skip.\n\t * @default ['node_modules/**']\n\t */\n\texclude?: string | string[]\n\t/**\n\t * Warn when multiple entry files have shebangs.\n\t * @default true\n\t */\n\twarnOnMultiple?: boolean\n}\n\ninterface ModuleInfo {\n\tshebang: string\n\tid: string\n}\n\ninterface PluginApi {\n\tgetShebang: (id: string) => string | undefined\n\tgetAllShebangs: () => Map<string, ModuleInfo>\n}\n\n// Package info for template variables\ndeclare const __PACKAGE_NAME__: string\ndeclare const __PACKAGE_VERSION__: string\n\nconst PACKAGE_NAME = 'rollup-plugin-replace-shebang'\nconst PACKAGE_VERSION = '2.0.0'\n\n/**\n * Validates if a string is a valid shebang.\n * Valid shebangs start with #! followed by a path.\n */\nfunction validateShebang(shebang: string): boolean {\n\treturn /^#!\\/[^\\s]*\\S/.test(shebang)\n}\n\n/**\n * Creates a filter function from include/exclude patterns.\n */\nfunction createFilter(\n\tinclude: string | string[] | undefined,\n\texclude: string | string[] | undefined\n): (id: string) => boolean {\n\t// If no include/exclude specified, process all files except node_modules\n\tif (!include && !exclude) {\n\t\treturn (id: string) => !id.includes('node_modules')\n\t}\n\n\t// Simple string matching for include/exclude\n\tconst includePatterns = include ? (Array.isArray(include) ? include : [include]) : null\n\tconst excludePatterns = exclude ? (Array.isArray(exclude) ? exclude : [exclude]) : null\n\n\t/**\n\t * Check if a pattern matches the given id\n\t */\n\tfunction matchPattern(pattern: string, id: string): boolean {\n\t\t// Exact match\n\t\tif (id === pattern) return true\n\t\t// Extension match (e.g., **/*.ts)\n\t\tif (pattern.startsWith('**/*.') && id.endsWith(pattern.slice(4))) return true\n\t\t// Prefix match (e.g., src/**)\n\t\tif (pattern.endsWith('/**') && id.startsWith(pattern.slice(0, -3))) return true\n\t\t// Directory contains match (e.g., **/node_modules/**)\n\t\tif (pattern.startsWith('**/') && pattern.endsWith('/**')) {\n\t\t\tconst segment = pattern.slice(3, -3)\n\t\t\treturn id.includes(`/${segment}/`) || id.startsWith(`${segment}/`)\n\t\t}\n\t\t// Wildcard match\n\t\tif (pattern.includes('*')) {\n\t\t\treturn patternToRegex(pattern).test(id)\n\t\t}\n\t\t// Plain string match - check if id contains the pattern as a path segment\n\t\treturn id.includes(`/${pattern}/`) || id.includes(pattern)\n\t}\n\n\treturn (id: string) => {\n\t\t// Check exclude first\n\t\tif (excludePatterns) {\n\t\t\tfor (const pattern of excludePatterns) {\n\t\t\t\tif (matchPattern(pattern, id)) return false\n\t\t\t}\n\t\t}\n\n\t\t// Check include\n\t\tif (includePatterns) {\n\t\t\tfor (const pattern of includePatterns) {\n\t\t\t\tif (matchPattern(pattern, id)) return true\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\n\t\treturn true\n\t}\n}\n\n/**\n * Converts a glob pattern to RegExp.\n */\nfunction patternToRegex(pattern: string): RegExp {\n\t// Handle **/ at the start (matches zero or more directories)\n\t// **/*.js should match: test.js, src/test.js, src/dir/test.js\n\tconst regex = pattern\n\t\t.replace(/\\*\\*\\//g, '<<DOUBLE_STAR_SLASH>>')\n\t\t.replace(/\\*\\*/g, '<<DOUBLE_STAR>>')\n\t\t.replace(/\\*/g, '[^/]*')\n\t\t.replace(/<<DOUBLE_STAR_SLASH>>/g, '(.*\\\\/)?')\n\t\t.replace(/<<DOUBLE_STAR>>/g, '.*')\n\t\t.replace(/\\?/g, '[^/]')\n\t\t.replace(/\\./g, '\\\\.')\n\t\t.replace(/\\//g, '\\\\/')\n\treturn new RegExp(`^${regex}$`)\n}\n\n/**\n * Resolves template variables in shebang string.\n */\nfunction resolveTemplateVars(shebang: string, vars: Record<string, string>): string {\n\treturn shebang.replace(/\\$\\{(\\w+)\\}/g, (_, key) => vars[key] || '')\n}\n\n/**\n * A Rollup plugin that preserves and relocates shebang (#!) to the output bundle.\n *\n * During the build process, shebangs are removed from individual modules and\n * re-added to the final bundle, ensuring CLI tools work correctly.\n *\n * @param options - Plugin configuration options\n * @returns A Rollup plugin instance\n *\n * @example\n * ```js\n * import replaceShebang from 'rollup-plugin-replace-shebang'\n *\n * export default {\n * plugins: [\n * replaceShebang({\n * shebang: '#!/usr/bin/env node',\n * skipBackslash: true,\n * chmod: true,\n * include: ['src/cli.ts'],\n * exclude: ['node_modules/**']\n * })\n * ]\n * }\n * ```\n */\nexport default function replaceShebangPlugin(options: Options = {}): Plugin & { api: PluginApi } {\n\tconst contextMap = new Map<string, ModuleInfo>()\n\tconst filter = createFilter(options.include, options.exclude)\n\tlet hasWarnedMultiple = false\n\n\tconst transform: TransformHook = (code, moduleID) => {\n\t\t// Skip if not matching filter\n\t\tif (!filter(moduleID)) return null\n\n\t\t// Skip if preserve mode\n\t\tif (options.preserve) return null\n\n\t\tlet shebang: string | undefined,\n\t\t\tnewCode = code.replace(/^#![^\\n]*/, match => {\n\t\t\t\tshebang = match\n\t\t\t\treturn ''\n\t\t\t})\n\n\t\tif (options.skipBackslash) {\n\t\t\tnewCode = newCode.replace(/(\\\\u005c|\\\\\\\\)/g, () => '__u005c__')\n\t\t}\n\n\t\tif (!shebang) return null\n\n\t\t// Validate shebang\n\t\tif (!validateShebang(shebang)) {\n\t\t\tconsole.warn(`[${PACKAGE_NAME}] Invalid shebang format: \"${shebang}\" in ${moduleID}`)\n\t\t}\n\n\t\t// Normalize moduleID by stripping query parameters\n\t\tconst normalizedID = moduleID.replace(/\\?.+$/, '')\n\t\tcontextMap.set(normalizedID, { shebang, id: normalizedID })\n\n\t\t// Warn on multiple shebangs\n\t\tif (options.warnOnMultiple !== false && contextMap.size > 1 && !hasWarnedMultiple) {\n\t\t\tconsole.warn(\n\t\t\t\t`[${PACKAGE_NAME}] Multiple files with shebang detected. ` +\n\t\t\t\t\t`Only the entry file's shebang will be used in output.`\n\t\t\t)\n\t\t\thasWarnedMultiple = true\n\t\t}\n\n\t\treturn { code: newCode, map: null }\n\t}\n\n\tconst renderChunk: RenderChunkHook = (code, chunk, { sourcemap }) => {\n\t\tconst moduleID = chunk.facadeModuleId?.replace(/\\?.+$/, '')\n\t\tif (!moduleID) return null\n\n\t\tconst info = contextMap.get(moduleID)\n\t\tif (!info) return null\n\n\t\tlet finalCode = code,\n\t\t\tresolvedShebang = options.shebang ?? info.shebang\n\t\tif (options.skipBackslash) {\n\t\t\tfinalCode = finalCode.replace(/__u005c__/g, () => '\\\\u005c')\n\t\t}\n\t\tresolvedShebang = resolveTemplateVars(resolvedShebang, {\n\t\t\tname: PACKAGE_NAME,\n\t\t\tversion: PACKAGE_VERSION\n\t\t})\n\n\t\tconst ms = new MagicString(finalCode)\n\t\tms.prepend(`${resolvedShebang}\\n`)\n\n\t\treturn {\n\t\t\tcode: ms.toString(),\n\t\t\tmap: sourcemap ? ms.generateMap({ hires: true }) : null\n\t\t}\n\t}\n\n\tconst buildEnd = (): void => {\n\t\t// Note: Don't clear contextMap here, renderChunk still needs it\n\t\t// Clear it in writeBundle instead\n\t}\n\n\tconst writeBundle: Plugin['writeBundle'] = async (outputOptions, bundle) => {\n\t\t// Clear contextMap after all chunks are written\n\t\tcontextMap.clear()\n\n\t\t// Handle chmod option\n\t\tif (options.chmod) {\n\t\t\tfor (const fileName of Object.keys(bundle)) {\n\t\t\t\tif (fileName.endsWith('.js') || fileName.endsWith('.mjs') || fileName.endsWith('.cjs')) {\n\t\t\t\t\tconst chunk = bundle[fileName]\n\t\t\t\t\tif (chunk && 'code' in chunk) {\n\t\t\t\t\t\t// Check if this file has a shebang\n\t\t\t\t\t\tif (chunk.code.startsWith('#!')) {\n\t\t\t\t\t\t\tconst fs = await import('node:fs/promises')\n\t\t\t\t\t\t\tconst path = await import('node:path')\n\t\t\t\t\t\t\tconst outputPath = outputOptions.file\n\t\t\t\t\t\t\t\t? path.resolve(outputOptions.file)\n\t\t\t\t\t\t\t\t: path.join(outputOptions.dir || 'dist', fileName)\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait fs.chmod(outputPath, 0o755)\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t// Ignore chmod errors (e.g., on Windows)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Plugin API for external access\n\tconst api: PluginApi = {\n\t\tgetShebang: (id: string) => contextMap.get(id)?.shebang,\n\t\tgetAllShebangs: () => new Map(contextMap)\n\t}\n\n\treturn {\n\t\tname: 'replace-shebang',\n\t\tversion: PACKAGE_VERSION,\n\t\ttransform,\n\t\trenderChunk,\n\t\tbuildEnd,\n\t\twriteBundle,\n\t\tapi\n\t}\n}\n\n// Export types\nexport type { PluginApi }\n"],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+
2
+ import { Plugin } from "rollup";
3
+
4
+ //#region src/index.d.ts
5
+ interface Options {
6
+ /**
7
+ * Custom shebang to prepend to the output.
8
+ * Defaults to the original shebang from the source file.
9
+ * Supports template variables: ${name}, ${version}
10
+ */
11
+ shebang?: string;
12
+ /**
13
+ * Whether to skip backslash escape processing.
14
+ * When true, preserves `\u005c` escape sequences.
15
+ * @default false
16
+ */
17
+ skipBackslash?: boolean;
18
+ /**
19
+ * Preserve the original shebang without modification.
20
+ * @default false
21
+ */
22
+ preserve?: boolean;
23
+ /**
24
+ * Automatically set executable permission (chmod +x) on output files.
25
+ * Only works when output is a file, not stdout.
26
+ * @default false
27
+ */
28
+ chmod?: boolean;
29
+ /**
30
+ * Include pattern for files to process.
31
+ * @default ["**\/*.js", "**\/*.mjs", "**\/*.ts", "**\/*.mts", "**\/*.cjs", "**\/*.cts"]
32
+ */
33
+ include?: string | string[];
34
+ /**
35
+ * Exclude pattern for files to skip.
36
+ * @default ['node_modules/**']
37
+ */
38
+ exclude?: string | string[];
39
+ /**
40
+ * Warn when multiple entry files have shebangs.
41
+ * @default true
42
+ */
43
+ warnOnMultiple?: boolean;
44
+ }
45
+ interface ModuleInfo {
46
+ shebang: string;
47
+ id: string;
48
+ }
49
+ interface PluginApi {
50
+ getShebang: (id: string) => string | undefined;
51
+ getAllShebangs: () => Map<string, ModuleInfo>;
52
+ }
53
+ /**
54
+ * A Rollup plugin that preserves and relocates shebang (#!) to the output bundle.
55
+ *
56
+ * During the build process, shebangs are removed from individual modules and
57
+ * re-added to the final bundle, ensuring CLI tools work correctly.
58
+ *
59
+ * @param options - Plugin configuration options
60
+ * @returns A Rollup plugin instance
61
+ *
62
+ * @example
63
+ * ```js
64
+ * import replaceShebang from 'rollup-plugin-replace-shebang'
65
+ *
66
+ * export default {
67
+ * plugins: [
68
+ * replaceShebang({
69
+ * shebang: '#!/usr/bin/env node',
70
+ * skipBackslash: true,
71
+ * chmod: true,
72
+ * include: ['src/cli.ts'],
73
+ * exclude: ['node_modules/**']
74
+ * })
75
+ * ]
76
+ * }
77
+ * ```
78
+ */
79
+ declare function replaceShebangPlugin(options?: Options): Plugin & {
80
+ api: PluginApi;
81
+ };
82
+ //#endregion
83
+ export { Options, type PluginApi, replaceShebangPlugin as default };
84
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;;UAGiB,OAAA;;AAAjB;;;;EAMC,OAAA;EAMA;;;;;EAAA,aAAA;EA0Bc;;AACd;;EAtBA,QAAA;EAyBA;;AACE;;;EApBF,KAAA;EAwBA;;;;EAnBA,OAAA;EAoB4C;;AAAA;;EAf5C,OAAA;EA+IqD;;;;EA1IrD,cAAA;AAAA;AAAA,UAGS,UAAA;EACT,OAAA;EACA,EAAA;AAAA;AAAA,UAGS,SAAA;EACT,UAAA,GAAa,EAAA;EACb,cAAA,QAAsB,GAAA,SAAY,UAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgIX,oBAAA,CAAqB,OAAA,GAAS,OAAA,GAAe,MAAA;EAAW,GAAA,EAAK,SAAA;AAAA"}
@@ -0,0 +1,84 @@
1
+
2
+ import { Plugin } from "rollup";
3
+
4
+ //#region src/index.d.ts
5
+ interface Options {
6
+ /**
7
+ * Custom shebang to prepend to the output.
8
+ * Defaults to the original shebang from the source file.
9
+ * Supports template variables: ${name}, ${version}
10
+ */
11
+ shebang?: string;
12
+ /**
13
+ * Whether to skip backslash escape processing.
14
+ * When true, preserves `\u005c` escape sequences.
15
+ * @default false
16
+ */
17
+ skipBackslash?: boolean;
18
+ /**
19
+ * Preserve the original shebang without modification.
20
+ * @default false
21
+ */
22
+ preserve?: boolean;
23
+ /**
24
+ * Automatically set executable permission (chmod +x) on output files.
25
+ * Only works when output is a file, not stdout.
26
+ * @default false
27
+ */
28
+ chmod?: boolean;
29
+ /**
30
+ * Include pattern for files to process.
31
+ * @default ["**\/*.js", "**\/*.mjs", "**\/*.ts", "**\/*.mts", "**\/*.cjs", "**\/*.cts"]
32
+ */
33
+ include?: string | string[];
34
+ /**
35
+ * Exclude pattern for files to skip.
36
+ * @default ['node_modules/**']
37
+ */
38
+ exclude?: string | string[];
39
+ /**
40
+ * Warn when multiple entry files have shebangs.
41
+ * @default true
42
+ */
43
+ warnOnMultiple?: boolean;
44
+ }
45
+ interface ModuleInfo {
46
+ shebang: string;
47
+ id: string;
48
+ }
49
+ interface PluginApi {
50
+ getShebang: (id: string) => string | undefined;
51
+ getAllShebangs: () => Map<string, ModuleInfo>;
52
+ }
53
+ /**
54
+ * A Rollup plugin that preserves and relocates shebang (#!) to the output bundle.
55
+ *
56
+ * During the build process, shebangs are removed from individual modules and
57
+ * re-added to the final bundle, ensuring CLI tools work correctly.
58
+ *
59
+ * @param options - Plugin configuration options
60
+ * @returns A Rollup plugin instance
61
+ *
62
+ * @example
63
+ * ```js
64
+ * import replaceShebang from 'rollup-plugin-replace-shebang'
65
+ *
66
+ * export default {
67
+ * plugins: [
68
+ * replaceShebang({
69
+ * shebang: '#!/usr/bin/env node',
70
+ * skipBackslash: true,
71
+ * chmod: true,
72
+ * include: ['src/cli.ts'],
73
+ * exclude: ['node_modules/**']
74
+ * })
75
+ * ]
76
+ * }
77
+ * ```
78
+ */
79
+ declare function replaceShebangPlugin(options?: Options): Plugin & {
80
+ api: PluginApi;
81
+ };
82
+ //#endregion
83
+ export { Options, type PluginApi, replaceShebangPlugin as default };
84
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;UAGiB,OAAA;;AAAjB;;;;EAMC,OAAA;EAMA;;;;;EAAA,aAAA;EA0Bc;;AACd;;EAtBA,QAAA;EAyBA;;AACE;;;EApBF,KAAA;EAwBA;;;;EAnBA,OAAA;EAoB4C;;AAAA;;EAf5C,OAAA;EA+IqD;;;;EA1IrD,cAAA;AAAA;AAAA,UAGS,UAAA;EACT,OAAA;EACA,EAAA;AAAA;AAAA,UAGS,SAAA;EACT,UAAA,GAAa,EAAA;EACb,cAAA,QAAsB,GAAA,SAAY,UAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgIX,oBAAA,CAAqB,OAAA,GAAS,OAAA,GAAe,MAAA;EAAW,GAAA,EAAK,SAAA;AAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,174 @@
1
+ /*!
2
+ * rollup-plugin-replace-shebang v2.0.0
3
+ * (c) 2021-present saqqdy <https://github.com/saqqdy>
4
+ * Released under the MIT License.
5
+ */
6
+ import MagicString from "magic-string";
7
+
8
+ //#region src/index.ts
9
+ const PACKAGE_NAME = "rollup-plugin-replace-shebang";
10
+ const PACKAGE_VERSION = "2.0.0";
11
+ /**
12
+ * Validates if a string is a valid shebang.
13
+ * Valid shebangs start with #! followed by a path.
14
+ */
15
+ function validateShebang(shebang) {
16
+ return /^#!\/[^\s]*\S/.test(shebang);
17
+ }
18
+ /**
19
+ * Creates a filter function from include/exclude patterns.
20
+ */
21
+ function createFilter(include, exclude) {
22
+ if (!include && !exclude) return (id) => !id.includes("node_modules");
23
+ const includePatterns = include ? Array.isArray(include) ? include : [include] : null;
24
+ const excludePatterns = exclude ? Array.isArray(exclude) ? exclude : [exclude] : null;
25
+ /**
26
+ * Check if a pattern matches the given id
27
+ */
28
+ function matchPattern(pattern, id) {
29
+ if (id === pattern) return true;
30
+ if (pattern.startsWith("**/*.") && id.endsWith(pattern.slice(4))) return true;
31
+ if (pattern.endsWith("/**") && id.startsWith(pattern.slice(0, -3))) return true;
32
+ if (pattern.startsWith("**/") && pattern.endsWith("/**")) {
33
+ const segment = pattern.slice(3, -3);
34
+ return id.includes(`/${segment}/`) || id.startsWith(`${segment}/`);
35
+ }
36
+ if (pattern.includes("*")) return patternToRegex(pattern).test(id);
37
+ return id.includes(`/${pattern}/`) || id.includes(pattern);
38
+ }
39
+ return (id) => {
40
+ if (excludePatterns) {
41
+ for (const pattern of excludePatterns) if (matchPattern(pattern, id)) return false;
42
+ }
43
+ if (includePatterns) {
44
+ for (const pattern of includePatterns) if (matchPattern(pattern, id)) return true;
45
+ return false;
46
+ }
47
+ return true;
48
+ };
49
+ }
50
+ /**
51
+ * Converts a glob pattern to RegExp.
52
+ */
53
+ function patternToRegex(pattern) {
54
+ const regex = pattern.replace(/\*\*\//g, "<<DOUBLE_STAR_SLASH>>").replace(/\*\*/g, "<<DOUBLE_STAR>>").replace(/\*/g, "[^/]*").replace(/<<DOUBLE_STAR_SLASH>>/g, "(.*\\/)?").replace(/<<DOUBLE_STAR>>/g, ".*").replace(/\?/g, "[^/]").replace(/\./g, "\\.").replace(/\//g, "\\/");
55
+ return new RegExp(`^${regex}$`);
56
+ }
57
+ /**
58
+ * Resolves template variables in shebang string.
59
+ */
60
+ function resolveTemplateVars(shebang, vars) {
61
+ return shebang.replace(/\$\{(\w+)\}/g, (_, key) => vars[key] || "");
62
+ }
63
+ /**
64
+ * A Rollup plugin that preserves and relocates shebang (#!) to the output bundle.
65
+ *
66
+ * During the build process, shebangs are removed from individual modules and
67
+ * re-added to the final bundle, ensuring CLI tools work correctly.
68
+ *
69
+ * @param options - Plugin configuration options
70
+ * @returns A Rollup plugin instance
71
+ *
72
+ * @example
73
+ * ```js
74
+ * import replaceShebang from 'rollup-plugin-replace-shebang'
75
+ *
76
+ * export default {
77
+ * plugins: [
78
+ * replaceShebang({
79
+ * shebang: '#!/usr/bin/env node',
80
+ * skipBackslash: true,
81
+ * chmod: true,
82
+ * include: ['src/cli.ts'],
83
+ * exclude: ['node_modules/**']
84
+ * })
85
+ * ]
86
+ * }
87
+ * ```
88
+ */
89
+ function replaceShebangPlugin(options = {}) {
90
+ const contextMap = /* @__PURE__ */ new Map();
91
+ const filter = createFilter(options.include, options.exclude);
92
+ let hasWarnedMultiple = false;
93
+ const transform = (code, moduleID) => {
94
+ if (!filter(moduleID)) return null;
95
+ if (options.preserve) return null;
96
+ let shebang, newCode = code.replace(/^#![^\n]*/, (match) => {
97
+ shebang = match;
98
+ return "";
99
+ });
100
+ if (options.skipBackslash) newCode = newCode.replace(/(\\u005c|\\\\)/g, () => "__u005c__");
101
+ if (!shebang) return null;
102
+ if (!validateShebang(shebang)) console.warn(`[${PACKAGE_NAME}] Invalid shebang format: "${shebang}" in ${moduleID}`);
103
+ const normalizedID = moduleID.replace(/\?.+$/, "");
104
+ contextMap.set(normalizedID, {
105
+ shebang,
106
+ id: normalizedID
107
+ });
108
+ if (options.warnOnMultiple !== false && contextMap.size > 1 && !hasWarnedMultiple) {
109
+ console.warn(`[${PACKAGE_NAME}] Multiple files with shebang detected. Only the entry file's shebang will be used in output.`);
110
+ hasWarnedMultiple = true;
111
+ }
112
+ return {
113
+ code: newCode,
114
+ map: null
115
+ };
116
+ };
117
+ const renderChunk = (code, chunk, { sourcemap }) => {
118
+ var _chunk$facadeModuleId, _options$shebang;
119
+ const moduleID = (_chunk$facadeModuleId = chunk.facadeModuleId) === null || _chunk$facadeModuleId === void 0 ? void 0 : _chunk$facadeModuleId.replace(/\?.+$/, "");
120
+ if (!moduleID) return null;
121
+ const info = contextMap.get(moduleID);
122
+ if (!info) return null;
123
+ let finalCode = code, resolvedShebang = (_options$shebang = options.shebang) !== null && _options$shebang !== void 0 ? _options$shebang : info.shebang;
124
+ if (options.skipBackslash) finalCode = finalCode.replace(/__u005c__/g, () => "\\u005c");
125
+ resolvedShebang = resolveTemplateVars(resolvedShebang, {
126
+ name: PACKAGE_NAME,
127
+ version: PACKAGE_VERSION
128
+ });
129
+ const ms = new MagicString(finalCode);
130
+ ms.prepend(`${resolvedShebang}\n`);
131
+ return {
132
+ code: ms.toString(),
133
+ map: sourcemap ? ms.generateMap({ hires: true }) : null
134
+ };
135
+ };
136
+ const buildEnd = () => {};
137
+ const writeBundle = async (outputOptions, bundle) => {
138
+ contextMap.clear();
139
+ if (options.chmod) {
140
+ for (const fileName of Object.keys(bundle)) if (fileName.endsWith(".js") || fileName.endsWith(".mjs") || fileName.endsWith(".cjs")) {
141
+ const chunk = bundle[fileName];
142
+ if (chunk && "code" in chunk) {
143
+ if (chunk.code.startsWith("#!")) {
144
+ const fs = await import("node:fs/promises");
145
+ const path = await import("node:path");
146
+ const outputPath = outputOptions.file ? path.resolve(outputOptions.file) : path.join(outputOptions.dir || "dist", fileName);
147
+ try {
148
+ await fs.chmod(outputPath, 493);
149
+ } catch {}
150
+ }
151
+ }
152
+ }
153
+ }
154
+ };
155
+ return {
156
+ name: "replace-shebang",
157
+ version: PACKAGE_VERSION,
158
+ transform,
159
+ renderChunk,
160
+ buildEnd,
161
+ writeBundle,
162
+ api: {
163
+ getShebang: (id) => {
164
+ var _contextMap$get;
165
+ return (_contextMap$get = contextMap.get(id)) === null || _contextMap$get === void 0 ? void 0 : _contextMap$get.shebang;
166
+ },
167
+ getAllShebangs: () => new Map(contextMap)
168
+ }
169
+ };
170
+ }
171
+
172
+ //#endregion
173
+ export { replaceShebangPlugin as default };
174
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Plugin, RenderChunkHook, TransformHook } from 'rollup'\nimport MagicString from 'magic-string'\n\nexport interface Options {\n\t/**\n\t * Custom shebang to prepend to the output.\n\t * Defaults to the original shebang from the source file.\n\t * Supports template variables: ${name}, ${version}\n\t */\n\tshebang?: string\n\t/**\n\t * Whether to skip backslash escape processing.\n\t * When true, preserves `\\u005c` escape sequences.\n\t * @default false\n\t */\n\tskipBackslash?: boolean\n\t/**\n\t * Preserve the original shebang without modification.\n\t * @default false\n\t */\n\tpreserve?: boolean\n\t/**\n\t * Automatically set executable permission (chmod +x) on output files.\n\t * Only works when output is a file, not stdout.\n\t * @default false\n\t */\n\tchmod?: boolean\n\t/**\n\t * Include pattern for files to process.\n\t * @default [\"**\\/*.js\", \"**\\/*.mjs\", \"**\\/*.ts\", \"**\\/*.mts\", \"**\\/*.cjs\", \"**\\/*.cts\"]\n\t */\n\tinclude?: string | string[]\n\t/**\n\t * Exclude pattern for files to skip.\n\t * @default ['node_modules/**']\n\t */\n\texclude?: string | string[]\n\t/**\n\t * Warn when multiple entry files have shebangs.\n\t * @default true\n\t */\n\twarnOnMultiple?: boolean\n}\n\ninterface ModuleInfo {\n\tshebang: string\n\tid: string\n}\n\ninterface PluginApi {\n\tgetShebang: (id: string) => string | undefined\n\tgetAllShebangs: () => Map<string, ModuleInfo>\n}\n\n// Package info for template variables\ndeclare const __PACKAGE_NAME__: string\ndeclare const __PACKAGE_VERSION__: string\n\nconst PACKAGE_NAME = 'rollup-plugin-replace-shebang'\nconst PACKAGE_VERSION = '2.0.0'\n\n/**\n * Validates if a string is a valid shebang.\n * Valid shebangs start with #! followed by a path.\n */\nfunction validateShebang(shebang: string): boolean {\n\treturn /^#!\\/[^\\s]*\\S/.test(shebang)\n}\n\n/**\n * Creates a filter function from include/exclude patterns.\n */\nfunction createFilter(\n\tinclude: string | string[] | undefined,\n\texclude: string | string[] | undefined\n): (id: string) => boolean {\n\t// If no include/exclude specified, process all files except node_modules\n\tif (!include && !exclude) {\n\t\treturn (id: string) => !id.includes('node_modules')\n\t}\n\n\t// Simple string matching for include/exclude\n\tconst includePatterns = include ? (Array.isArray(include) ? include : [include]) : null\n\tconst excludePatterns = exclude ? (Array.isArray(exclude) ? exclude : [exclude]) : null\n\n\t/**\n\t * Check if a pattern matches the given id\n\t */\n\tfunction matchPattern(pattern: string, id: string): boolean {\n\t\t// Exact match\n\t\tif (id === pattern) return true\n\t\t// Extension match (e.g., **/*.ts)\n\t\tif (pattern.startsWith('**/*.') && id.endsWith(pattern.slice(4))) return true\n\t\t// Prefix match (e.g., src/**)\n\t\tif (pattern.endsWith('/**') && id.startsWith(pattern.slice(0, -3))) return true\n\t\t// Directory contains match (e.g., **/node_modules/**)\n\t\tif (pattern.startsWith('**/') && pattern.endsWith('/**')) {\n\t\t\tconst segment = pattern.slice(3, -3)\n\t\t\treturn id.includes(`/${segment}/`) || id.startsWith(`${segment}/`)\n\t\t}\n\t\t// Wildcard match\n\t\tif (pattern.includes('*')) {\n\t\t\treturn patternToRegex(pattern).test(id)\n\t\t}\n\t\t// Plain string match - check if id contains the pattern as a path segment\n\t\treturn id.includes(`/${pattern}/`) || id.includes(pattern)\n\t}\n\n\treturn (id: string) => {\n\t\t// Check exclude first\n\t\tif (excludePatterns) {\n\t\t\tfor (const pattern of excludePatterns) {\n\t\t\t\tif (matchPattern(pattern, id)) return false\n\t\t\t}\n\t\t}\n\n\t\t// Check include\n\t\tif (includePatterns) {\n\t\t\tfor (const pattern of includePatterns) {\n\t\t\t\tif (matchPattern(pattern, id)) return true\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\n\t\treturn true\n\t}\n}\n\n/**\n * Converts a glob pattern to RegExp.\n */\nfunction patternToRegex(pattern: string): RegExp {\n\t// Handle **/ at the start (matches zero or more directories)\n\t// **/*.js should match: test.js, src/test.js, src/dir/test.js\n\tconst regex = pattern\n\t\t.replace(/\\*\\*\\//g, '<<DOUBLE_STAR_SLASH>>')\n\t\t.replace(/\\*\\*/g, '<<DOUBLE_STAR>>')\n\t\t.replace(/\\*/g, '[^/]*')\n\t\t.replace(/<<DOUBLE_STAR_SLASH>>/g, '(.*\\\\/)?')\n\t\t.replace(/<<DOUBLE_STAR>>/g, '.*')\n\t\t.replace(/\\?/g, '[^/]')\n\t\t.replace(/\\./g, '\\\\.')\n\t\t.replace(/\\//g, '\\\\/')\n\treturn new RegExp(`^${regex}$`)\n}\n\n/**\n * Resolves template variables in shebang string.\n */\nfunction resolveTemplateVars(shebang: string, vars: Record<string, string>): string {\n\treturn shebang.replace(/\\$\\{(\\w+)\\}/g, (_, key) => vars[key] || '')\n}\n\n/**\n * A Rollup plugin that preserves and relocates shebang (#!) to the output bundle.\n *\n * During the build process, shebangs are removed from individual modules and\n * re-added to the final bundle, ensuring CLI tools work correctly.\n *\n * @param options - Plugin configuration options\n * @returns A Rollup plugin instance\n *\n * @example\n * ```js\n * import replaceShebang from 'rollup-plugin-replace-shebang'\n *\n * export default {\n * plugins: [\n * replaceShebang({\n * shebang: '#!/usr/bin/env node',\n * skipBackslash: true,\n * chmod: true,\n * include: ['src/cli.ts'],\n * exclude: ['node_modules/**']\n * })\n * ]\n * }\n * ```\n */\nexport default function replaceShebangPlugin(options: Options = {}): Plugin & { api: PluginApi } {\n\tconst contextMap = new Map<string, ModuleInfo>()\n\tconst filter = createFilter(options.include, options.exclude)\n\tlet hasWarnedMultiple = false\n\n\tconst transform: TransformHook = (code, moduleID) => {\n\t\t// Skip if not matching filter\n\t\tif (!filter(moduleID)) return null\n\n\t\t// Skip if preserve mode\n\t\tif (options.preserve) return null\n\n\t\tlet shebang: string | undefined,\n\t\t\tnewCode = code.replace(/^#![^\\n]*/, match => {\n\t\t\t\tshebang = match\n\t\t\t\treturn ''\n\t\t\t})\n\n\t\tif (options.skipBackslash) {\n\t\t\tnewCode = newCode.replace(/(\\\\u005c|\\\\\\\\)/g, () => '__u005c__')\n\t\t}\n\n\t\tif (!shebang) return null\n\n\t\t// Validate shebang\n\t\tif (!validateShebang(shebang)) {\n\t\t\tconsole.warn(`[${PACKAGE_NAME}] Invalid shebang format: \"${shebang}\" in ${moduleID}`)\n\t\t}\n\n\t\t// Normalize moduleID by stripping query parameters\n\t\tconst normalizedID = moduleID.replace(/\\?.+$/, '')\n\t\tcontextMap.set(normalizedID, { shebang, id: normalizedID })\n\n\t\t// Warn on multiple shebangs\n\t\tif (options.warnOnMultiple !== false && contextMap.size > 1 && !hasWarnedMultiple) {\n\t\t\tconsole.warn(\n\t\t\t\t`[${PACKAGE_NAME}] Multiple files with shebang detected. ` +\n\t\t\t\t\t`Only the entry file's shebang will be used in output.`\n\t\t\t)\n\t\t\thasWarnedMultiple = true\n\t\t}\n\n\t\treturn { code: newCode, map: null }\n\t}\n\n\tconst renderChunk: RenderChunkHook = (code, chunk, { sourcemap }) => {\n\t\tconst moduleID = chunk.facadeModuleId?.replace(/\\?.+$/, '')\n\t\tif (!moduleID) return null\n\n\t\tconst info = contextMap.get(moduleID)\n\t\tif (!info) return null\n\n\t\tlet finalCode = code,\n\t\t\tresolvedShebang = options.shebang ?? info.shebang\n\t\tif (options.skipBackslash) {\n\t\t\tfinalCode = finalCode.replace(/__u005c__/g, () => '\\\\u005c')\n\t\t}\n\t\tresolvedShebang = resolveTemplateVars(resolvedShebang, {\n\t\t\tname: PACKAGE_NAME,\n\t\t\tversion: PACKAGE_VERSION\n\t\t})\n\n\t\tconst ms = new MagicString(finalCode)\n\t\tms.prepend(`${resolvedShebang}\\n`)\n\n\t\treturn {\n\t\t\tcode: ms.toString(),\n\t\t\tmap: sourcemap ? ms.generateMap({ hires: true }) : null\n\t\t}\n\t}\n\n\tconst buildEnd = (): void => {\n\t\t// Note: Don't clear contextMap here, renderChunk still needs it\n\t\t// Clear it in writeBundle instead\n\t}\n\n\tconst writeBundle: Plugin['writeBundle'] = async (outputOptions, bundle) => {\n\t\t// Clear contextMap after all chunks are written\n\t\tcontextMap.clear()\n\n\t\t// Handle chmod option\n\t\tif (options.chmod) {\n\t\t\tfor (const fileName of Object.keys(bundle)) {\n\t\t\t\tif (fileName.endsWith('.js') || fileName.endsWith('.mjs') || fileName.endsWith('.cjs')) {\n\t\t\t\t\tconst chunk = bundle[fileName]\n\t\t\t\t\tif (chunk && 'code' in chunk) {\n\t\t\t\t\t\t// Check if this file has a shebang\n\t\t\t\t\t\tif (chunk.code.startsWith('#!')) {\n\t\t\t\t\t\t\tconst fs = await import('node:fs/promises')\n\t\t\t\t\t\t\tconst path = await import('node:path')\n\t\t\t\t\t\t\tconst outputPath = outputOptions.file\n\t\t\t\t\t\t\t\t? path.resolve(outputOptions.file)\n\t\t\t\t\t\t\t\t: path.join(outputOptions.dir || 'dist', fileName)\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait fs.chmod(outputPath, 0o755)\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t// Ignore chmod errors (e.g., on Windows)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Plugin API for external access\n\tconst api: PluginApi = {\n\t\tgetShebang: (id: string) => contextMap.get(id)?.shebang,\n\t\tgetAllShebangs: () => new Map(contextMap)\n\t}\n\n\treturn {\n\t\tname: 'replace-shebang',\n\t\tversion: PACKAGE_VERSION,\n\t\ttransform,\n\t\trenderChunk,\n\t\tbuildEnd,\n\t\twriteBundle,\n\t\tapi\n\t}\n}\n\n// Export types\nexport type { PluginApi }\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,104 +1,79 @@
1
1
  {
2
- "name": "rollup-plugin-replace-shebang",
3
- "description": "一个自动替换shebang的rollup插件",
4
- "version": "1.2.0",
5
- "packageManager": "pnpm@7.7.0",
6
- "main": "lib/index.js",
7
- "module": "es/index.mjs",
8
- "typings": "typings/index.d.ts",
9
- "exports": {
10
- "import": "./es/index.mjs",
11
- "default": "./lib/index.js"
12
- },
13
- "files": [
14
- "dist",
15
- "es",
16
- "lib",
17
- "typings"
18
- ],
19
- "scripts": {
20
- "release": "npm run -s prepare && npm test && git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags && npm publish",
21
- "gen:version": "tscjs scripts/version",
22
- "build": "rimraf dist es lib && rollup -c",
23
- "build:types": "run-s build-temp-types roll-types",
24
- "build-temp-types": "tsc --emitDeclarationOnly --outDir temp/ -p src/",
25
- "patch-types": "node script/patchTypes",
26
- "roll-types": "api-extractor run && rimraf temp",
27
- "dev": "rollup -c -w",
28
- "pub": "tscjs scripts/publish",
29
- "unpub": "tscjs scripts/unpublish",
30
- "sync": "tscjs scripts/sync",
31
- "dist": "run-s eslint prettier build build:types",
32
- "docs": "rimraf docs && typedoc",
33
- "eslint": "eslint --fix --ext .ts,.js",
34
- "prettier": "prettier --write \"**/*.{js,ts,json,md}\"",
35
- "prettier:docs": "prettier --write \"**/*.md\""
36
- },
37
- "directories": {
38
- "es": "es",
39
- "lib": "lib",
40
- "dist": "dist",
41
- "src": "src",
42
- "typings": "typings"
43
- },
44
- "dependencies": {
45
- "magic-string": "^0.26.2"
46
- },
47
- "devDependencies": {
48
- "@babel/core": "^7.18.13",
49
- "@babel/preset-env": "^7.18.10",
50
- "@babel/preset-typescript": "^7.18.6",
51
- "@eslint-sets/eslint-config-simple-ts": "^3.0.4",
52
- "@microsoft/api-extractor": "^7.29.5",
53
- "@rollup/plugin-babel": "^5.3.1",
54
- "@rollup/plugin-commonjs": "^22.0.2",
55
- "@rollup/plugin-node-resolve": "^13.3.0",
56
- "@types/node": "^18.7.13",
57
- "esbuild": "^0.15.5",
58
- "eslint": "^8.22.0",
59
- "npm-run-all": "^4.1.5",
60
- "prettier": "^2.7.1",
61
- "prettier-config-common": "^1.2.1",
62
- "rimraf": "^3.0.2",
63
- "rollup": "^2.78.1",
64
- "rollup-plugin-terser": "^7.0.2",
65
- "rollup-plugin-typescript2": "^0.33.0",
66
- "rollup-plugin-visualizer": "^5.8.0",
67
- "tsnd": "^1.1.0",
68
- "typedoc": "^0.23.10",
69
- "typedoc-plugin-markdown": "^3.13.4",
70
- "typescript": "^4.7.4"
71
- },
72
- "peerDependencies": {
73
- "rollup": ">= 2.0.0"
74
- },
75
- "keywords": [
76
- "rollup",
77
- "plugin",
78
- "replace",
79
- "shebang",
80
- "hashbang",
81
- "saqqdy"
82
- ],
83
- "pnpm": {
84
- "peerDependencyRules": {
85
- "ignoreMissing": [
86
- "tslib",
87
- "webpack"
88
- ],
89
- "allowedVersions": {
90
- "eslint": "^8.0.0"
91
- }
92
- }
93
- },
94
- "license": "MIT",
95
- "author": "saqqdy <saqqdy@qq.com> (https://github.com/saqqdy)",
96
- "repository": {
97
- "type": "git",
98
- "url": "git+https://github.com/saqqdy/rollup-plugin-replace-shebang.git"
99
- },
100
- "bugs": {
101
- "url": "https://github.com/saqqdy/rollup-plugin-replace-shebang/issues"
102
- },
103
- "homepage": "https://github.com/saqqdy/rollup-plugin-replace-shebang#readme"
104
- }
2
+ "name": "rollup-plugin-replace-shebang",
3
+ "description": "A Rollup plugin that preserves and relocates shebang to the output bundle.",
4
+ "version": "2.0.0",
5
+ "type": "module",
6
+ "main": "dist/index.cjs",
7
+ "module": "dist/index.mjs",
8
+ "types": "dist/index.d.mts",
9
+ "exports": {
10
+ ".": {
11
+ "types": {
12
+ "import": "./dist/index.d.mts",
13
+ "require": "./dist/index.d.cts"
14
+ },
15
+ "import": "./dist/index.mjs",
16
+ "require": "./dist/index.cjs"
17
+ }
18
+ },
19
+ "engines": {
20
+ "node": ">=12"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "dependencies": {
26
+ "magic-string": "^0.30.21"
27
+ },
28
+ "devDependencies": {
29
+ "@eslint-sets/eslint-config": "^6.2.0",
30
+ "@types/node": "^24.12.0",
31
+ "@vitest/coverage-v8": "^3.2.4",
32
+ "eslint": "^9.39.4",
33
+ "rollup": "^4.59.1",
34
+ "tsdown": "^0.21.4",
35
+ "tsx": "^4.21.0",
36
+ "typescript": "^5.9.3",
37
+ "vitepress": "^1.6.4",
38
+ "vitest": "^3.2.4"
39
+ },
40
+ "peerDependencies": {
41
+ "rollup": ">=2.0.0"
42
+ },
43
+ "keywords": [
44
+ "rollup",
45
+ "rollup-plugin",
46
+ "shebang",
47
+ "hashbang",
48
+ "cli",
49
+ "bundler"
50
+ ],
51
+ "sideEffects": false,
52
+ "license": "MIT",
53
+ "author": "saqqdy <https://github.com/saqqdy>",
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "git+https://github.com/saqqdy/rollup-plugin-replace-shebang.git"
57
+ },
58
+ "bugs": {
59
+ "url": "https://github.com/saqqdy/rollup-plugin-replace-shebang/issues"
60
+ },
61
+ "homepage": "https://github.com/saqqdy/rollup-plugin-replace-shebang#readme",
62
+ "scripts": {
63
+ "build": "tsdown",
64
+ "dev": "tsdown --watch",
65
+ "docs:dev": "vitepress dev docs",
66
+ "docs:build": "vitepress build docs",
67
+ "docs:preview": "vitepress preview docs",
68
+ "deploy": "sh scripts/deploy.sh",
69
+ "test": "vitest run",
70
+ "test:watch": "vitest",
71
+ "test:coverage": "vitest run --coverage",
72
+ "test:compat": "npx tsx scripts/test-compat.ts",
73
+ "pub": "tscjs scripts/publish",
74
+ "unpub": "tscjs scripts/unpublish",
75
+ "sync": "tscjs scripts/sync",
76
+ "lint": "eslint --fix",
77
+ "dist": "pnpm run lint && pnpm run build"
78
+ }
79
+ }
package/es/index.mjs DELETED
@@ -1,59 +0,0 @@
1
- import MagicString from 'magic-string';
2
-
3
- /**
4
- * 一个自动替换shebang的rollup插件
5
- *
6
- * @param options - 配置参数
7
- * @returns Plugin - 插件
8
- */
9
-
10
- function replaceStringPlugin(options = {}) {
11
- const contextMap = new Map();
12
- return {
13
- name: 'replace-shebang',
14
-
15
- transform(code, moduleID) {
16
- let shebang;
17
- code = code.replace(/^#![^\n]*/, match => (shebang = match, ''));
18
-
19
- if (options.skipBackslash) {
20
- code = code.replace(/(\\u005c|\\\\)/g, () => '__u005c__');
21
- }
22
-
23
- if (!shebang) return null;
24
- contextMap.set(moduleID, {
25
- shebang
26
- });
27
- return {
28
- code,
29
- map: null
30
- };
31
- },
32
-
33
- renderChunk(code, chunk, {
34
- sourcemap
35
- }) {
36
- const moduleID = chunk.facadeModuleId.replace(/\?.+$/, '');
37
- const {
38
- shebang
39
- } = contextMap.get(moduleID) || {};
40
- if (!shebang) return null;
41
-
42
- if (options.skipBackslash) {
43
- code = code.replace(/__u005c__/g, () => '\\u005c');
44
- }
45
-
46
- const ms = new MagicString(code);
47
- ms.prepend(`${options.shebang || shebang}\n`);
48
- return {
49
- code: ms.toString(),
50
- map: sourcemap ? ms.generateMap({
51
- hires: true
52
- }) : null
53
- };
54
- }
55
-
56
- };
57
- }
58
-
59
- export { replaceStringPlugin as default };
package/lib/index.js DELETED
@@ -1,65 +0,0 @@
1
- 'use strict';
2
-
3
- var MagicString = require('magic-string');
4
-
5
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
6
-
7
- var MagicString__default = /*#__PURE__*/_interopDefaultLegacy(MagicString);
8
-
9
- /**
10
- * 一个自动替换shebang的rollup插件
11
- *
12
- * @param options - 配置参数
13
- * @returns Plugin - 插件
14
- */
15
-
16
- function replaceStringPlugin(options = {}) {
17
- const contextMap = new Map();
18
- return {
19
- name: 'replace-shebang',
20
-
21
- transform(code, moduleID) {
22
- let shebang;
23
- code = code.replace(/^#![^\n]*/, match => (shebang = match, ''));
24
-
25
- if (options.skipBackslash) {
26
- code = code.replace(/(\\u005c|\\\\)/g, () => '__u005c__');
27
- }
28
-
29
- if (!shebang) return null;
30
- contextMap.set(moduleID, {
31
- shebang
32
- });
33
- return {
34
- code,
35
- map: null
36
- };
37
- },
38
-
39
- renderChunk(code, chunk, {
40
- sourcemap
41
- }) {
42
- const moduleID = chunk.facadeModuleId.replace(/\?.+$/, '');
43
- const {
44
- shebang
45
- } = contextMap.get(moduleID) || {};
46
- if (!shebang) return null;
47
-
48
- if (options.skipBackslash) {
49
- code = code.replace(/__u005c__/g, () => '\\u005c');
50
- }
51
-
52
- const ms = new MagicString__default["default"](code);
53
- ms.prepend(`${options.shebang || shebang}\n`);
54
- return {
55
- code: ms.toString(),
56
- map: sourcemap ? ms.generateMap({
57
- hires: true
58
- }) : null
59
- };
60
- }
61
-
62
- };
63
- }
64
-
65
- module.exports = replaceStringPlugin;
package/typings/base.d.ts DELETED
@@ -1,9 +0,0 @@
1
- export type AnyObject = Record<string, any>
2
-
3
- export interface AnyFunction extends AnyObject {
4
- (...args: any[]): any
5
- }
6
-
7
- export type ArrayOneMore<T> = {
8
- 0: T
9
- } & Array<T>
@@ -1,17 +0,0 @@
1
- import type { Plugin as Plugin_2 } from 'rollup';
2
-
3
- export declare interface Options {
4
- shebang?: string;
5
- skipBackslash?: boolean;
6
- }
7
-
8
- /**
9
- * 一个自动替换shebang的rollup插件
10
- *
11
- * @param options - 配置参数
12
- * @returns Plugin - 插件
13
- */
14
- declare function replaceStringPlugin(options?: Options): Plugin_2;
15
- export default replaceStringPlugin;
16
-
17
- export { }