barrelize 1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Nizami
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # Barrelize
2
+
3
+ 🚀 A modern, lightweight and efficient tool for automatically generating index (barrel) files in your JavaScript and TypeScript projects.
4
+
5
+ Barrelize simplifies module exports by creating clean, centralized `index.js` or `index.ts` files, reducing boilerplate and improving project organization.
6
+
7
+ [![NPM Version](https://img.shields.io/npm/v/barrelize)](https://www.npmjs.com/package/barrelize)
8
+
9
+ [![GitHub License](https://img.shields.io/github/license/nizami/barrelize)
10
+ ](https://opensource.org/licenses/MIT)
11
+
12
+ ## Features
13
+ - **Automatic Barrel Generation**: Scans directories and creates index files with exports for all modules.
14
+ - **TypeScript Support**: Seamlessly works with TypeScript projects, preserving type safety.
15
+ - **Customizable**: Configure file patterns, ignore specific files, or customize export styles (named, default, or both).
16
+ - **Recursive**: Optionally generate barrels for nested directories.
17
+ - **CLI & API**: Use via command line for quick setups or integrate programmatically in your build scripts.
18
+
19
+ ### Why Use Barrelize?
20
+ - **Save Time**: Eliminate manual creation and maintenance of barrel files.
21
+ - **Cleaner Imports**: Simplify import statements with a single entry point for each directory.
22
+ - **Scalable**: Ideal for large projects with complex folder structures.
23
+
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ npm install -D barrelize
29
+ ```
30
+
31
+ ## Quick Start
32
+
33
+ 1. Initialize configuration (optional):
34
+
35
+ ```bash
36
+ npx barrelize init
37
+ ```
38
+
39
+ 2. Generate barrel files:
40
+
41
+ ```bash
42
+ npx barrelize
43
+ ```
44
+
45
+ ## CLI Commands
46
+
47
+ ### `barrelize init [config path]`
48
+
49
+ Creates a `.barrelize` configuration file in your project.
50
+
51
+ ```bash
52
+ npx barrelize init # Creates .barrelize in current directory
53
+ npx barrelize init barrelize.json # Creates config at specified path
54
+ ```
55
+
56
+ ### `barrelize [config path]`
57
+
58
+ Generates barrel (index) files based on your configuration.
59
+
60
+ ```bash
61
+ npx barrelize # Uses default .barrelize config
62
+ npx barrelize -c barrelize.json # Uses custom config file
63
+ ```
64
+
65
+ ## Configuration
66
+
67
+ Create a `.barrelize` file in your project root:
68
+
69
+ ```json
70
+ {
71
+ "directories": [
72
+ {
73
+ "path": "src",
74
+ "include": ["**/*.ts", "**/*.tsx"],
75
+ "exclude": ["**/*.test.ts", "**/*.spec.ts"],
76
+ "order": ["types", "constants", "utils"],
77
+ "keepFileExtension": true,
78
+ "replace": [
79
+ {
80
+ "find": ".ts$",
81
+ "replacement": ""
82
+ }
83
+ ]
84
+ }
85
+ ]
86
+ }
87
+ ```
88
+
89
+ ## Example
90
+
91
+ Before:
92
+
93
+ ```
94
+ src/
95
+ ├── types.ts
96
+ ├── constants.ts
97
+ ├── utils.ts
98
+ └── components/
99
+ ├── Button.tsx
100
+ └── Input.tsx
101
+ ```
102
+
103
+ After running `barrelize`:
104
+
105
+ ```
106
+ src/
107
+ ├── types.ts
108
+ ├── constants.ts
109
+ ├── utils.ts
110
+ ├── index.ts # New!
111
+ └── components/
112
+ ├── Button.tsx
113
+ └── Input.tsx
114
+ ```
115
+
116
+ Generated `src/index.ts`:
117
+
118
+ ```typescript
119
+ export * from './types';
120
+ export * from './constants';
121
+ export * from './utils';
122
+ export * from './components/Button.tsx';
123
+ export * from './components/Input.tsx';
124
+ ```
125
+
126
+ ## Contributing
127
+
128
+ Contributions are welcome! Please feel free to submit a Pull Request.
129
+
130
+ ## License
131
+
132
+ MIT © Nizami
package/bin/cli.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {cliInit} from '../lib/index.js';
4
+
5
+ cliInit();
package/lib/index.js ADDED
@@ -0,0 +1,771 @@
1
+ import JSON5 from "json5";
2
+ import { writeFile, readFile } from "node:fs/promises";
3
+ import { glob } from "glob";
4
+ import { existsSync } from "node:fs";
5
+ import { resolve, dirname, join } from "node:path";
6
+ import { cac } from "cac";
7
+ const name = "barrelize";
8
+ const version = "1.0.0";
9
+ function cliInit() {
10
+ const cli = cac(name);
11
+ cli.command("[config path]", `Generate 'index.ts' files for all directories`).action(async (config) => {
12
+ await runGenerateCommand({ configPath: config || ".barrelize" });
13
+ });
14
+ cli.command("init [config path]", "Create .barrelize config file if does not exist").example("barrelize init").example("barrelize init .barrelize").example("barrelize init root/.barrelize").action(async (path = ".barrelize") => {
15
+ await runInitCommand(path);
16
+ });
17
+ cli.help();
18
+ cli.version(version);
19
+ try {
20
+ cli.parse();
21
+ } catch (error) {
22
+ if (error instanceof Error) {
23
+ logError(error.message);
24
+ } else {
25
+ logError(String(error));
26
+ }
27
+ }
28
+ }
29
+ var _accessExpressionAsString = {};
30
+ var hasRequired_accessExpressionAsString;
31
+ function require_accessExpressionAsString() {
32
+ if (hasRequired_accessExpressionAsString) return _accessExpressionAsString;
33
+ hasRequired_accessExpressionAsString = 1;
34
+ Object.defineProperty(_accessExpressionAsString, "__esModule", { value: true });
35
+ _accessExpressionAsString._accessExpressionAsString = void 0;
36
+ const _accessExpressionAsString$1 = (str) => variable(str) ? `.${str}` : `[${JSON.stringify(str)}]`;
37
+ _accessExpressionAsString._accessExpressionAsString = _accessExpressionAsString$1;
38
+ const variable = (str) => reserved(str) === false && /^[a-zA-Z_$][a-zA-Z_$0-9]*$/g.test(str);
39
+ const reserved = (str) => RESERVED.has(str);
40
+ const RESERVED = /* @__PURE__ */ new Set([
41
+ "break",
42
+ "case",
43
+ "catch",
44
+ "class",
45
+ "const",
46
+ "continue",
47
+ "debugger",
48
+ "default",
49
+ "delete",
50
+ "do",
51
+ "else",
52
+ "enum",
53
+ "export",
54
+ "extends",
55
+ "false",
56
+ "finally",
57
+ "for",
58
+ "function",
59
+ "if",
60
+ "import",
61
+ "in",
62
+ "instanceof",
63
+ "new",
64
+ "null",
65
+ "return",
66
+ "super",
67
+ "switch",
68
+ "this",
69
+ "throw",
70
+ "true",
71
+ "try",
72
+ "typeof",
73
+ "var",
74
+ "void",
75
+ "while",
76
+ "with"
77
+ ]);
78
+ return _accessExpressionAsString;
79
+ }
80
+ var _accessExpressionAsStringExports = /* @__PURE__ */ require_accessExpressionAsString();
81
+ var _validateReport = {};
82
+ var hasRequired_validateReport;
83
+ function require_validateReport() {
84
+ if (hasRequired_validateReport) return _validateReport;
85
+ hasRequired_validateReport = 1;
86
+ Object.defineProperty(_validateReport, "__esModule", { value: true });
87
+ _validateReport._validateReport = void 0;
88
+ const _validateReport$1 = (array) => {
89
+ const reportable = (path) => {
90
+ if (array.length === 0)
91
+ return true;
92
+ const last = array[array.length - 1].path;
93
+ return path.length > last.length || last.substring(0, path.length) !== path;
94
+ };
95
+ return (exceptable, error) => {
96
+ if (exceptable && reportable(error.path))
97
+ array.push(error);
98
+ return false;
99
+ };
100
+ };
101
+ _validateReport._validateReport = _validateReport$1;
102
+ return _validateReport;
103
+ }
104
+ var _validateReportExports = /* @__PURE__ */ require_validateReport();
105
+ var _createStandardSchema = {};
106
+ var hasRequired_createStandardSchema;
107
+ function require_createStandardSchema() {
108
+ if (hasRequired_createStandardSchema) return _createStandardSchema;
109
+ hasRequired_createStandardSchema = 1;
110
+ Object.defineProperty(_createStandardSchema, "__esModule", { value: true });
111
+ _createStandardSchema._createStandardSchema = void 0;
112
+ const _createStandardSchema$1 = (fn) => Object.assign(fn, {
113
+ "~standard": {
114
+ version: 1,
115
+ vendor: "typia",
116
+ validate: (input) => {
117
+ const result = fn(input);
118
+ if (result.success) {
119
+ return {
120
+ value: result.data
121
+ };
122
+ } else {
123
+ return {
124
+ issues: result.errors.map((error) => ({
125
+ message: `expected ${error.expected}, got ${error.value}`,
126
+ path: typiaPathToStandardSchemaPath(error.path)
127
+ }))
128
+ };
129
+ }
130
+ }
131
+ }
132
+ });
133
+ _createStandardSchema._createStandardSchema = _createStandardSchema$1;
134
+ var PathParserState;
135
+ (function(PathParserState2) {
136
+ PathParserState2[PathParserState2["Start"] = 0] = "Start";
137
+ PathParserState2[PathParserState2["Property"] = 1] = "Property";
138
+ PathParserState2[PathParserState2["StringKey"] = 2] = "StringKey";
139
+ PathParserState2[PathParserState2["NumberKey"] = 3] = "NumberKey";
140
+ })(PathParserState || (PathParserState = {}));
141
+ const typiaPathToStandardSchemaPath = (path) => {
142
+ if (!path.startsWith("$input")) {
143
+ throw new Error(`Invalid path: ${JSON.stringify(path)}`);
144
+ }
145
+ const segments = [];
146
+ let currentSegment = "";
147
+ let state = PathParserState.Start;
148
+ let index = "$input".length - 1;
149
+ while (index < path.length - 1) {
150
+ index++;
151
+ const char = path[index];
152
+ if (state === PathParserState.Property) {
153
+ if (char === "." || char === "[") {
154
+ segments.push({
155
+ key: currentSegment
156
+ });
157
+ state = PathParserState.Start;
158
+ } else if (index === path.length - 1) {
159
+ currentSegment += char;
160
+ segments.push({
161
+ key: currentSegment
162
+ });
163
+ index++;
164
+ state = PathParserState.Start;
165
+ } else {
166
+ currentSegment += char;
167
+ }
168
+ } else if (state === PathParserState.StringKey) {
169
+ if (char === '"') {
170
+ segments.push({
171
+ key: JSON.parse(currentSegment + char)
172
+ });
173
+ index += 2;
174
+ state = PathParserState.Start;
175
+ } else if (char === "\\") {
176
+ currentSegment += path[index];
177
+ index++;
178
+ currentSegment += path[index];
179
+ } else {
180
+ currentSegment += char;
181
+ }
182
+ } else if (state === PathParserState.NumberKey) {
183
+ if (char === "]") {
184
+ segments.push({
185
+ key: Number.parseInt(currentSegment)
186
+ });
187
+ index++;
188
+ state = PathParserState.Start;
189
+ } else {
190
+ currentSegment += char;
191
+ }
192
+ }
193
+ if (state === PathParserState.Start && index < path.length - 1) {
194
+ const newChar = path[index];
195
+ currentSegment = "";
196
+ if (newChar === "[") {
197
+ if (path[index + 1] === '"') {
198
+ state = PathParserState.StringKey;
199
+ index++;
200
+ currentSegment = '"';
201
+ } else {
202
+ state = PathParserState.NumberKey;
203
+ }
204
+ } else if (newChar === ".") {
205
+ state = PathParserState.Property;
206
+ } else {
207
+ throw new Error("Unreachable: pointer points invalid character");
208
+ }
209
+ }
210
+ }
211
+ if (state !== PathParserState.Start) {
212
+ throw new Error(`Failed to parse path: ${JSON.stringify(path)}`);
213
+ }
214
+ return segments;
215
+ };
216
+ return _createStandardSchema;
217
+ }
218
+ var _createStandardSchemaExports = /* @__PURE__ */ require_createStandardSchema();
219
+ const validateGenerateOptions = (() => {
220
+ const _io0 = (input, _exceptionable = true) => "string" === typeof input.configPath && (1 === Object.keys(input).length || Object.keys(input).every((key) => {
221
+ if (["configPath"].some((prop) => key === prop))
222
+ return true;
223
+ const value = input[key];
224
+ if (void 0 === value)
225
+ return true;
226
+ return false;
227
+ }));
228
+ const _vo0 = (input, _path, _exceptionable = true) => ["string" === typeof input.configPath || _report(_exceptionable, {
229
+ path: _path + ".configPath",
230
+ expected: "string",
231
+ value: input.configPath
232
+ }), 1 === Object.keys(input).length || (false === _exceptionable || Object.keys(input).map((key) => {
233
+ if (["configPath"].some((prop) => key === prop))
234
+ return true;
235
+ const value = input[key];
236
+ if (void 0 === value)
237
+ return true;
238
+ return _report(_exceptionable, {
239
+ path: _path + _accessExpressionAsStringExports._accessExpressionAsString(key),
240
+ expected: "undefined",
241
+ value
242
+ });
243
+ }).every((flag) => flag))].every((flag) => flag);
244
+ const __is = (input, _exceptionable = true) => "object" === typeof input && null !== input && _io0(input, true);
245
+ let errors;
246
+ let _report;
247
+ return _createStandardSchemaExports._createStandardSchema((input) => {
248
+ if (false === __is(input)) {
249
+ errors = [];
250
+ _report = _validateReportExports._validateReport(errors);
251
+ ((input2, _path, _exceptionable = true) => ("object" === typeof input2 && null !== input2 || _report(true, {
252
+ path: _path + "",
253
+ expected: "GenerateCommandOptions",
254
+ value: input2
255
+ })) && _vo0(input2, _path + "", true) || _report(true, {
256
+ path: _path + "",
257
+ expected: "GenerateCommandOptions",
258
+ value: input2
259
+ }))(input, "$input", true);
260
+ const success = 0 === errors.length;
261
+ return success ? {
262
+ success,
263
+ data: input
264
+ } : {
265
+ success,
266
+ errors,
267
+ data: input
268
+ };
269
+ }
270
+ return {
271
+ success: true,
272
+ data: input
273
+ };
274
+ });
275
+ })();
276
+ async function runGenerateCommand(options) {
277
+ const validatedOptions = validateGenerateOptions(options);
278
+ if (!validatedOptions.success) {
279
+ logValidationError(`Invalid 'generate' command options`, validatedOptions);
280
+ return;
281
+ }
282
+ if (!existsSync(options.configPath)) {
283
+ logError(`Couldn't find barrelize config file with path '${options.configPath}'`);
284
+ return;
285
+ }
286
+ const config = await parseConfig(options.configPath);
287
+ const validatedConfig = validateConfig(config);
288
+ if (!validatedConfig.success) {
289
+ logValidationError(`Invalid barrelize config`, validatedConfig);
290
+ return;
291
+ }
292
+ const rootPath = resolve(dirname(options.configPath));
293
+ await generateBarrels(rootPath, validatedConfig.data);
294
+ }
295
+ const configTemplate = {
296
+ directories: [
297
+ {
298
+ path: "src",
299
+ include: ["**/*.ts"],
300
+ exclude: ["**/*.test.ts"],
301
+ order: ["models", "api"],
302
+ indexFilePath: "index.ts"
303
+ }
304
+ ]
305
+ };
306
+ async function runInitCommand(baseConfigFilePath) {
307
+ const configFilePath = resolve(process.cwd(), baseConfigFilePath);
308
+ if (existsSync(configFilePath)) {
309
+ logWarning(`Config file '${configFilePath}' already exists`);
310
+ return;
311
+ }
312
+ const configDirectoryPath = dirname(configFilePath);
313
+ if (!existsSync(configDirectoryPath)) {
314
+ logWarning(`Directory '${configDirectoryPath}' does not exist`);
315
+ return;
316
+ }
317
+ const configTemplateJson = JSON.stringify(configTemplate, null, 2);
318
+ await writeFile(configFilePath, configTemplateJson);
319
+ console.log(colorize(baseConfigFilePath, TerminalColor.CYAN), colorize(`file created`, TerminalColor.GRAY));
320
+ console.log(colorize(configTemplateJson, TerminalColor.GREEN));
321
+ }
322
+ const validateConfig = (() => {
323
+ const _io0 = (input, _exceptionable = true) => Array.isArray(input.directories) && input.directories.every((elem, _index1) => "object" === typeof elem && null !== elem && _io1(elem, _exceptionable)) && (1 === Object.keys(input).length || Object.keys(input).every((key) => {
324
+ if (["directories"].some((prop) => key === prop))
325
+ return true;
326
+ const value = input[key];
327
+ if (void 0 === value)
328
+ return true;
329
+ return false;
330
+ }));
331
+ const _io1 = (input, _exceptionable = true) => "string" === typeof input.path && (Array.isArray(input.include) && input.include.every((elem, _index2) => "string" === typeof elem)) && (Array.isArray(input.exclude) && input.exclude.every((elem, _index3) => "string" === typeof elem)) && (void 0 === input.order || Array.isArray(input.order) && input.order.every((elem, _index4) => "string" === typeof elem)) && (void 0 === input.indexFilePath || "string" === typeof input.indexFilePath) && (void 0 === input.keepFileExtension || "boolean" === typeof input.keepFileExtension) && (void 0 === input.replace || Array.isArray(input.replace) && input.replace.every((elem, _index5) => "object" === typeof elem && null !== elem && _io2(elem, _exceptionable))) && (3 === Object.keys(input).length || Object.keys(input).every((key) => {
332
+ if (["path", "include", "exclude", "order", "indexFilePath", "keepFileExtension", "replace"].some((prop) => key === prop))
333
+ return true;
334
+ const value = input[key];
335
+ if (void 0 === value)
336
+ return true;
337
+ return false;
338
+ }));
339
+ const _io2 = (input, _exceptionable = true) => "string" === typeof input.find && "string" === typeof input.replacement && (2 === Object.keys(input).length || Object.keys(input).every((key) => {
340
+ if (["find", "replacement"].some((prop) => key === prop))
341
+ return true;
342
+ const value = input[key];
343
+ if (void 0 === value)
344
+ return true;
345
+ return false;
346
+ }));
347
+ const _vo0 = (input, _path, _exceptionable = true) => [(Array.isArray(input.directories) || _report(_exceptionable, {
348
+ path: _path + ".directories",
349
+ expected: "Array<__type>",
350
+ value: input.directories
351
+ })) && input.directories.map((elem, _index6) => ("object" === typeof elem && null !== elem || _report(_exceptionable, {
352
+ path: _path + ".directories[" + _index6 + "]",
353
+ expected: "__type",
354
+ value: elem
355
+ })) && _vo1(elem, _path + ".directories[" + _index6 + "]", _exceptionable) || _report(_exceptionable, {
356
+ path: _path + ".directories[" + _index6 + "]",
357
+ expected: "__type",
358
+ value: elem
359
+ })).every((flag) => flag) || _report(_exceptionable, {
360
+ path: _path + ".directories",
361
+ expected: "Array<__type>",
362
+ value: input.directories
363
+ }), 1 === Object.keys(input).length || (false === _exceptionable || Object.keys(input).map((key) => {
364
+ if (["directories"].some((prop) => key === prop))
365
+ return true;
366
+ const value = input[key];
367
+ if (void 0 === value)
368
+ return true;
369
+ return _report(_exceptionable, {
370
+ path: _path + _accessExpressionAsStringExports._accessExpressionAsString(key),
371
+ expected: "undefined",
372
+ value
373
+ });
374
+ }).every((flag) => flag))].every((flag) => flag);
375
+ const _vo1 = (input, _path, _exceptionable = true) => ["string" === typeof input.path || _report(_exceptionable, {
376
+ path: _path + ".path",
377
+ expected: "string",
378
+ value: input.path
379
+ }), (Array.isArray(input.include) || _report(_exceptionable, {
380
+ path: _path + ".include",
381
+ expected: "Array<string>",
382
+ value: input.include
383
+ })) && input.include.map((elem, _index7) => "string" === typeof elem || _report(_exceptionable, {
384
+ path: _path + ".include[" + _index7 + "]",
385
+ expected: "string",
386
+ value: elem
387
+ })).every((flag) => flag) || _report(_exceptionable, {
388
+ path: _path + ".include",
389
+ expected: "Array<string>",
390
+ value: input.include
391
+ }), (Array.isArray(input.exclude) || _report(_exceptionable, {
392
+ path: _path + ".exclude",
393
+ expected: "Array<string>",
394
+ value: input.exclude
395
+ })) && input.exclude.map((elem, _index8) => "string" === typeof elem || _report(_exceptionable, {
396
+ path: _path + ".exclude[" + _index8 + "]",
397
+ expected: "string",
398
+ value: elem
399
+ })).every((flag) => flag) || _report(_exceptionable, {
400
+ path: _path + ".exclude",
401
+ expected: "Array<string>",
402
+ value: input.exclude
403
+ }), void 0 === input.order || (Array.isArray(input.order) || _report(_exceptionable, {
404
+ path: _path + ".order",
405
+ expected: "(Array<string> | undefined)",
406
+ value: input.order
407
+ })) && input.order.map((elem, _index9) => "string" === typeof elem || _report(_exceptionable, {
408
+ path: _path + ".order[" + _index9 + "]",
409
+ expected: "string",
410
+ value: elem
411
+ })).every((flag) => flag) || _report(_exceptionable, {
412
+ path: _path + ".order",
413
+ expected: "(Array<string> | undefined)",
414
+ value: input.order
415
+ }), void 0 === input.indexFilePath || "string" === typeof input.indexFilePath || _report(_exceptionable, {
416
+ path: _path + ".indexFilePath",
417
+ expected: "(string | undefined)",
418
+ value: input.indexFilePath
419
+ }), void 0 === input.keepFileExtension || "boolean" === typeof input.keepFileExtension || _report(_exceptionable, {
420
+ path: _path + ".keepFileExtension",
421
+ expected: "(boolean | undefined)",
422
+ value: input.keepFileExtension
423
+ }), void 0 === input.replace || (Array.isArray(input.replace) || _report(_exceptionable, {
424
+ path: _path + ".replace",
425
+ expected: "(Array<__type>.o1 | undefined)",
426
+ value: input.replace
427
+ })) && input.replace.map((elem, _index10) => ("object" === typeof elem && null !== elem || _report(_exceptionable, {
428
+ path: _path + ".replace[" + _index10 + "]",
429
+ expected: "__type.o1",
430
+ value: elem
431
+ })) && _vo2(elem, _path + ".replace[" + _index10 + "]", _exceptionable) || _report(_exceptionable, {
432
+ path: _path + ".replace[" + _index10 + "]",
433
+ expected: "__type.o1",
434
+ value: elem
435
+ })).every((flag) => flag) || _report(_exceptionable, {
436
+ path: _path + ".replace",
437
+ expected: "(Array<__type>.o1 | undefined)",
438
+ value: input.replace
439
+ }), 3 === Object.keys(input).length || (false === _exceptionable || Object.keys(input).map((key) => {
440
+ if (["path", "include", "exclude", "order", "indexFilePath", "keepFileExtension", "replace"].some((prop) => key === prop))
441
+ return true;
442
+ const value = input[key];
443
+ if (void 0 === value)
444
+ return true;
445
+ return _report(_exceptionable, {
446
+ path: _path + _accessExpressionAsStringExports._accessExpressionAsString(key),
447
+ expected: "undefined",
448
+ value
449
+ });
450
+ }).every((flag) => flag))].every((flag) => flag);
451
+ const _vo2 = (input, _path, _exceptionable = true) => ["string" === typeof input.find || _report(_exceptionable, {
452
+ path: _path + ".find",
453
+ expected: "string",
454
+ value: input.find
455
+ }), "string" === typeof input.replacement || _report(_exceptionable, {
456
+ path: _path + ".replacement",
457
+ expected: "string",
458
+ value: input.replacement
459
+ }), 2 === Object.keys(input).length || (false === _exceptionable || Object.keys(input).map((key) => {
460
+ if (["find", "replacement"].some((prop) => key === prop))
461
+ return true;
462
+ const value = input[key];
463
+ if (void 0 === value)
464
+ return true;
465
+ return _report(_exceptionable, {
466
+ path: _path + _accessExpressionAsStringExports._accessExpressionAsString(key),
467
+ expected: "undefined",
468
+ value
469
+ });
470
+ }).every((flag) => flag))].every((flag) => flag);
471
+ const __is = (input, _exceptionable = true) => "object" === typeof input && null !== input && _io0(input, true);
472
+ let errors;
473
+ let _report;
474
+ return _createStandardSchemaExports._createStandardSchema((input) => {
475
+ if (false === __is(input)) {
476
+ errors = [];
477
+ _report = _validateReportExports._validateReport(errors);
478
+ ((input2, _path, _exceptionable = true) => ("object" === typeof input2 && null !== input2 || _report(true, {
479
+ path: _path + "",
480
+ expected: "Config",
481
+ value: input2
482
+ })) && _vo0(input2, _path + "", true) || _report(true, {
483
+ path: _path + "",
484
+ expected: "Config",
485
+ value: input2
486
+ }))(input, "$input", true);
487
+ const success = 0 === errors.length;
488
+ return success ? {
489
+ success,
490
+ data: input
491
+ } : {
492
+ success,
493
+ errors,
494
+ data: input
495
+ };
496
+ }
497
+ return {
498
+ success: true,
499
+ data: input
500
+ };
501
+ });
502
+ })();
503
+ const validateOutputConfig = (() => {
504
+ const _io0 = (input, _exceptionable = true) => "string" === typeof input.cwd && (Array.isArray(input.directories) && input.directories.every((elem, _index1) => "object" === typeof elem && null !== elem && _io1(elem, _exceptionable))) && (2 === Object.keys(input).length || Object.keys(input).every((key) => {
505
+ if (["cwd", "directories"].some((prop) => key === prop))
506
+ return true;
507
+ const value = input[key];
508
+ if (void 0 === value)
509
+ return true;
510
+ return false;
511
+ }));
512
+ const _io1 = (input, _exceptionable = true) => Array.isArray(input.files) && input.files.every((elem, _index2) => "string" === typeof elem) && (void 0 === input.order || Array.isArray(input.order) && input.order.every((elem, _index3) => "string" === typeof elem)) && (1 === Object.keys(input).length || Object.keys(input).every((key) => {
513
+ if (["files", "order"].some((prop) => key === prop))
514
+ return true;
515
+ const value = input[key];
516
+ if (void 0 === value)
517
+ return true;
518
+ return false;
519
+ }));
520
+ const _vo0 = (input, _path, _exceptionable = true) => ["string" === typeof input.cwd || _report(_exceptionable, {
521
+ path: _path + ".cwd",
522
+ expected: "string",
523
+ value: input.cwd
524
+ }), (Array.isArray(input.directories) || _report(_exceptionable, {
525
+ path: _path + ".directories",
526
+ expected: "Array<__type>",
527
+ value: input.directories
528
+ })) && input.directories.map((elem, _index4) => ("object" === typeof elem && null !== elem || _report(_exceptionable, {
529
+ path: _path + ".directories[" + _index4 + "]",
530
+ expected: "__type",
531
+ value: elem
532
+ })) && _vo1(elem, _path + ".directories[" + _index4 + "]", _exceptionable) || _report(_exceptionable, {
533
+ path: _path + ".directories[" + _index4 + "]",
534
+ expected: "__type",
535
+ value: elem
536
+ })).every((flag) => flag) || _report(_exceptionable, {
537
+ path: _path + ".directories",
538
+ expected: "Array<__type>",
539
+ value: input.directories
540
+ }), 2 === Object.keys(input).length || (false === _exceptionable || Object.keys(input).map((key) => {
541
+ if (["cwd", "directories"].some((prop) => key === prop))
542
+ return true;
543
+ const value = input[key];
544
+ if (void 0 === value)
545
+ return true;
546
+ return _report(_exceptionable, {
547
+ path: _path + _accessExpressionAsStringExports._accessExpressionAsString(key),
548
+ expected: "undefined",
549
+ value
550
+ });
551
+ }).every((flag) => flag))].every((flag) => flag);
552
+ const _vo1 = (input, _path, _exceptionable = true) => [(Array.isArray(input.files) || _report(_exceptionable, {
553
+ path: _path + ".files",
554
+ expected: "Array<string>",
555
+ value: input.files
556
+ })) && input.files.map((elem, _index5) => "string" === typeof elem || _report(_exceptionable, {
557
+ path: _path + ".files[" + _index5 + "]",
558
+ expected: "string",
559
+ value: elem
560
+ })).every((flag) => flag) || _report(_exceptionable, {
561
+ path: _path + ".files",
562
+ expected: "Array<string>",
563
+ value: input.files
564
+ }), void 0 === input.order || (Array.isArray(input.order) || _report(_exceptionable, {
565
+ path: _path + ".order",
566
+ expected: "(Array<string> | undefined)",
567
+ value: input.order
568
+ })) && input.order.map((elem, _index6) => "string" === typeof elem || _report(_exceptionable, {
569
+ path: _path + ".order[" + _index6 + "]",
570
+ expected: "string",
571
+ value: elem
572
+ })).every((flag) => flag) || _report(_exceptionable, {
573
+ path: _path + ".order",
574
+ expected: "(Array<string> | undefined)",
575
+ value: input.order
576
+ }), 1 === Object.keys(input).length || (false === _exceptionable || Object.keys(input).map((key) => {
577
+ if (["files", "order"].some((prop) => key === prop))
578
+ return true;
579
+ const value = input[key];
580
+ if (void 0 === value)
581
+ return true;
582
+ return _report(_exceptionable, {
583
+ path: _path + _accessExpressionAsStringExports._accessExpressionAsString(key),
584
+ expected: "undefined",
585
+ value
586
+ });
587
+ }).every((flag) => flag))].every((flag) => flag);
588
+ const __is = (input, _exceptionable = true) => "object" === typeof input && null !== input && _io0(input, true);
589
+ let errors;
590
+ let _report;
591
+ return _createStandardSchemaExports._createStandardSchema((input) => {
592
+ if (false === __is(input)) {
593
+ errors = [];
594
+ _report = _validateReportExports._validateReport(errors);
595
+ ((input2, _path, _exceptionable = true) => ("object" === typeof input2 && null !== input2 || _report(true, {
596
+ path: _path + "",
597
+ expected: "OutputConfig",
598
+ value: input2
599
+ })) && _vo0(input2, _path + "", true) || _report(true, {
600
+ path: _path + "",
601
+ expected: "OutputConfig",
602
+ value: input2
603
+ }))(input, "$input", true);
604
+ const success = 0 === errors.length;
605
+ return success ? {
606
+ success,
607
+ data: input
608
+ } : {
609
+ success,
610
+ errors,
611
+ data: input
612
+ };
613
+ }
614
+ return {
615
+ success: true,
616
+ data: input
617
+ };
618
+ });
619
+ })();
620
+ async function parseConfig(configPath) {
621
+ const configJson = (await readFile(configPath)).toString();
622
+ try {
623
+ return JSON5.parse(configJson);
624
+ } catch (error) {
625
+ if (error instanceof SyntaxError) {
626
+ const referenceMatches = error.message.match(/at (\d+:\d+)/);
627
+ const reasonMatches = error.message.match(/(?<=JSON5: ).*?(?= at \d+:\d+)/);
628
+ if (referenceMatches && reasonMatches) {
629
+ const reference = `${configPath}:${referenceMatches[1]}`;
630
+ const reason = reasonMatches[0];
631
+ logError(`Barrelize json config syntax error: ${reason} at ${reference}:`);
632
+ return null;
633
+ }
634
+ }
635
+ console.error(error);
636
+ return null;
637
+ }
638
+ }
639
+ const DEFAULT_INDEX_FILE_PATH = "index.ts";
640
+ async function generateBarrels(rootPath, config) {
641
+ for (const directoryConfig of config.directories) {
642
+ const indexFileBasePath = directoryConfig.indexFilePath ?? DEFAULT_INDEX_FILE_PATH;
643
+ const indexFileRelativePath = join(directoryConfig.path, indexFileBasePath);
644
+ const indexFileAbsolutePath = resolve(rootPath, indexFileRelativePath);
645
+ const indexDirectory = dirname(indexFileAbsolutePath);
646
+ if (!existsSync(indexDirectory)) {
647
+ console.error(logWarning(`Index directory '${indexDirectory}' does not exist - skipping`));
648
+ console.error(logWarning(` Please verify the directory path in your configuration`));
649
+ continue;
650
+ }
651
+ const files = await generateBarrel(rootPath, directoryConfig);
652
+ const content = files.map((x) => `export * from './${x}';`).join("\n");
653
+ await writeFile(indexFileAbsolutePath, content);
654
+ console.log(
655
+ colorize(indexFileRelativePath, TerminalColor.CYAN),
656
+ colorize(`exports ${files.length} file${files.length > 1 ? "s" : ""}`, TerminalColor.GRAY)
657
+ );
658
+ }
659
+ }
660
+ async function generateBarrel(rootPath, directoryConfig) {
661
+ const ignore = [...directoryConfig.exclude, "**/index.ts", "**/index.js"];
662
+ const cwd = resolve(rootPath, directoryConfig.path);
663
+ let files = await glob(directoryConfig.include, { cwd, ignore, nodir: true, includeChildMatches: true });
664
+ files = files.map((file) => file.replace(/\\/g, "/"));
665
+ files = handleFileExtension(directoryConfig, files);
666
+ files = handleOrder(directoryConfig, files);
667
+ files = handlePathReplacement(directoryConfig, files);
668
+ return files;
669
+ }
670
+ function handleFileExtension(config, files) {
671
+ if (!config.keepFileExtension) {
672
+ return files.map((file) => file.replace(/\.ts$/, ""));
673
+ }
674
+ return files;
675
+ }
676
+ function handlePathReplacement(config, files) {
677
+ if (!config.replace?.length) {
678
+ return files;
679
+ }
680
+ for (let i = 0; i < config.replace.length; i++) {
681
+ const { find, replacement } = config.replace[i];
682
+ try {
683
+ const regexp = new RegExp(find);
684
+ files = files.map((file) => file.replace(regexp, replacement));
685
+ } catch (error) {
686
+ if (error instanceof SyntaxError) {
687
+ logError(
688
+ error.message.replace(
689
+ "Invalid regular expression:",
690
+ `Invalid regular expression number ${i + 1} in config.`
691
+ )
692
+ );
693
+ } else {
694
+ logWarning(`Invalid regular expression number ${i + 1}`);
695
+ }
696
+ }
697
+ }
698
+ return files;
699
+ }
700
+ function handleOrder(config, files) {
701
+ files = files.sort((a, b) => a.localeCompare(b));
702
+ if (!config.order?.length) {
703
+ return files;
704
+ }
705
+ return files.sort((a, b) => sortPathWeight(config.order, b) - sortPathWeight(config.order, a));
706
+ }
707
+ function sortPathWeight(order, path) {
708
+ const orderIndex = order.findIndex((x) => path.startsWith(x));
709
+ if (orderIndex >= 0) {
710
+ return (order.length - orderIndex) * 100 - path.split("/").length;
711
+ }
712
+ return -path.split("/").length;
713
+ }
714
+ var TerminalColor = /* @__PURE__ */ ((TerminalColor2) => {
715
+ TerminalColor2[TerminalColor2["BLACK"] = 0] = "BLACK";
716
+ TerminalColor2[TerminalColor2["RED"] = 1] = "RED";
717
+ TerminalColor2[TerminalColor2["GREEN"] = 2] = "GREEN";
718
+ TerminalColor2[TerminalColor2["YELLOW"] = 3] = "YELLOW";
719
+ TerminalColor2[TerminalColor2["BLUE"] = 4] = "BLUE";
720
+ TerminalColor2[TerminalColor2["MAGENTA"] = 5] = "MAGENTA";
721
+ TerminalColor2[TerminalColor2["CYAN"] = 6] = "CYAN";
722
+ TerminalColor2[TerminalColor2["WHITE"] = 7] = "WHITE";
723
+ TerminalColor2[TerminalColor2["GRAY"] = 60] = "GRAY";
724
+ return TerminalColor2;
725
+ })(TerminalColor || {});
726
+ function colorize(text, foreground, background) {
727
+ if (background == null) {
728
+ return `\x1B[${30 + foreground}m${text}\x1B[0m`;
729
+ }
730
+ return `\x1B[${30 + foreground}m\x1B[${40 + background}m${text}\x1B[0m`;
731
+ }
732
+ function logError(message) {
733
+ console.log(colorize(message, TerminalColor.RED));
734
+ }
735
+ function logWarning(message) {
736
+ console.log(colorize(message, TerminalColor.YELLOW));
737
+ }
738
+ function logInfo(message) {
739
+ console.log(colorize(message, TerminalColor.BLUE));
740
+ }
741
+ function logValidationError(message, validation) {
742
+ const formatError = (e) => {
743
+ const property = e.path.replace("$input.", "");
744
+ if (e.expected === "undefined") {
745
+ return `Property '${property}' is not allowed in configuration`;
746
+ }
747
+ if (e.value === void 0) {
748
+ return `Missing required property '${property}' in configuration`;
749
+ }
750
+ return `Invalid type for property '${property}'`;
751
+ };
752
+ const errors = validation.errors.map((x) => ` ` + formatError(x)).join("\n");
753
+ const text = `${message}:
754
+ ${errors}`;
755
+ logError(text);
756
+ }
757
+ export {
758
+ TerminalColor,
759
+ cliInit,
760
+ colorize,
761
+ generateBarrels,
762
+ logError,
763
+ logInfo,
764
+ logValidationError,
765
+ logWarning,
766
+ parseConfig,
767
+ runGenerateCommand,
768
+ runInitCommand,
769
+ validateConfig,
770
+ validateOutputConfig
771
+ };
@@ -0,0 +1 @@
1
+ export declare function cliInit(): void;
@@ -0,0 +1,3 @@
1
+ export type GenerateCommandOptions = {
2
+ configPath: string;
3
+ };
@@ -0,0 +1,2 @@
1
+ import { GenerateCommandOptions } from '../../index.ts';
2
+ export declare function runGenerateCommand(options: GenerateCommandOptions): Promise<void>;
@@ -0,0 +1 @@
1
+ export declare function runInitCommand(baseConfigFilePath: string): Promise<void>;
@@ -0,0 +1,15 @@
1
+ export type Config = {
2
+ directories: {
3
+ path: string;
4
+ include: string[];
5
+ exclude: string[];
6
+ order?: string[];
7
+ indexFilePath?: string;
8
+ keepFileExtension?: boolean;
9
+ replace?: {
10
+ find: string;
11
+ replacement: string;
12
+ }[];
13
+ }[];
14
+ };
15
+ export declare const validateConfig: ((input: unknown) => import('typia').IValidation<Config>) & import('@standard-schema/spec').StandardSchemaV1<unknown, Config>;
@@ -0,0 +1,8 @@
1
+ export type OutputConfig = {
2
+ cwd: string;
3
+ directories: {
4
+ files: string[];
5
+ order?: string[];
6
+ }[];
7
+ };
8
+ export declare const validateOutputConfig: ((input: unknown) => import('typia').IValidation<OutputConfig>) & import('@standard-schema/spec').StandardSchemaV1<unknown, OutputConfig>;
@@ -0,0 +1,2 @@
1
+ import { Config } from '../index.ts';
2
+ export declare function parseConfig(configPath: string): Promise<Config | null>;
@@ -0,0 +1,2 @@
1
+ import { Config } from '../index.ts';
2
+ export declare function generateBarrels(rootPath: string, config: Config): Promise<void>;
@@ -0,0 +1,11 @@
1
+ export * from './cli/cli';
2
+ export * from './cli/commands/generate-command';
3
+ export * from './cli/commands/generate-command-options';
4
+ export * from './cli/commands/init-command';
5
+ export * from './config/config';
6
+ export * from './config/output-config';
7
+ export * from './config/parse-config';
8
+ export * from './generate/generate-barrels';
9
+ export * from './log/colorize';
10
+ export * from './log/log';
11
+ export * from './log/log-validation-error';
@@ -0,0 +1,12 @@
1
+ export declare enum TerminalColor {
2
+ BLACK = 0,
3
+ RED = 1,
4
+ GREEN = 2,
5
+ YELLOW = 3,
6
+ BLUE = 4,
7
+ MAGENTA = 5,
8
+ CYAN = 6,
9
+ WHITE = 7,
10
+ GRAY = 60
11
+ }
12
+ export declare function colorize(text: string, foreground: TerminalColor, background?: TerminalColor): string;
@@ -0,0 +1,2 @@
1
+ import { IValidation } from 'typia';
2
+ export declare function logValidationError(message: string, validation: IValidation.IFailure): void;
@@ -0,0 +1,3 @@
1
+ export declare function logError(message: string): void;
2
+ export declare function logWarning(message: string): void;
3
+ export declare function logInfo(message: string): void;
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "barrelize",
3
+ "version": "1.0.0",
4
+ "description": "Automatically generating index (barrel) files",
5
+ "scripts": {
6
+ "build": "vite build",
7
+ "prepare": "ts-patch install",
8
+ "test": "vitest run"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git@github.com/nizami/barrelize"
13
+ },
14
+ "keywords": [
15
+ "barrels",
16
+ "typescript",
17
+ "index",
18
+ "file"
19
+ ],
20
+ "author": "Nizami",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "cac": "^6.7.14",
24
+ "glob": "^11.0.2",
25
+ "json5": "^2.2.3"
26
+ },
27
+ "devDependencies": {
28
+ "@ryoppippi/unplugin-typia": "^2.2.1",
29
+ "@types/node": "^22.15.17",
30
+ "ts-patch": "^3.3.0",
31
+ "typescript": "~5.8.3",
32
+ "typia": "^9.2.0",
33
+ "vite": "^6.3.5",
34
+ "vite-plugin-dts": "^4.5.3",
35
+ "vitest": "^3.1.3"
36
+ },
37
+ "bin": {
38
+ "barrelize": "./bin/cli.js"
39
+ },
40
+ "type": "module",
41
+ "sideEffects": false,
42
+ "files": [
43
+ "bin",
44
+ "lib"
45
+ ],
46
+ "exports": {
47
+ ".": {
48
+ "types": "./lib/src/index.d.ts",
49
+ "import": "./lib/index.js"
50
+ }
51
+ }
52
+ }