Skip to content

Commit 0b8d341

Browse files
committed
Merge branch 'barrel'
2 parents 4af1047 + c26d8b7 commit 0b8d341

120 files changed

Lines changed: 8922 additions & 41 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.changeset/green-sails-sneeze.md‎

Lines changed: 0 additions & 5 deletions
This file was deleted.

‎.changeset/remove-dependency-reports.md‎

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Implementation Log: `barrel` Command
2+
3+
## 2026-07-10
4+
5+
- Started implementation from `plan.md`.
6+
- Confirmed the command should be specifier-aware and should not reuse
7+
`listIncomingDeps(...)` directly because that loses default/namespace import
8+
details.
9+
- Added `src/commands/barrel.ts`, CLI wiring, and public API export.
10+
- Build issue: `pnpm build` tried to recreate `node_modules`; sandboxed network
11+
failed with npm registry DNS errors. Reran with network approval and restored
12+
dependencies.
13+
- TypeScript bug fixed: a local `PositionedNode` helper initially used an
14+
interface extending Babel's `Node` union; changed it to an intersection type.
15+
- Added API tests for named imports, default imports, namespace imports,
16+
type-only imports, re-export sources, skipped dynamic/side-effect imports,
17+
collision rejection, and idempotent existing-index merges.
18+
- Added compiled CLI fixtures for apply, dry-run JSON, include/ignore,
19+
idempotence, warnings, name collisions, and sibling `index.*` conflicts.
20+
- Added README documentation and a changeset for the new command.
21+
- Lint issue fixed: removed an unused helper after `oxlint` reported it.
22+
- Verification:
23+
- `CI=true pnpm test:run` passed.
24+
- `CI=true pnpm test:dist` passed.
25+
- `CI=true pnpm build` passed.
26+
- `CI=true pnpm lint` passed with no warnings after cleanup.

‎.tasks/05uoc-barrel/plan.md‎

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
# Plan: `barrel` Command
2+
3+
## Goal
4+
5+
Add `dosido barrel <directory>` as a mutating command that:
6+
7+
- finds incoming static references from outside a directory to files inside it,
8+
- rewrites supported references to point at the directory itself,
9+
- creates or updates `<directory>/index.ts`,
10+
- exports only the names required by those rewritten references,
11+
- honors `--root`, `--dry-run`, `--json`, `--include`, and `--ignore`.
12+
13+
## Accepted Semantics
14+
15+
- Rewrite only references from outside the target directory. Leave internal
16+
references alone.
17+
- Use `<directory>/index.ts` as the barrel file.
18+
- If a sibling barrel file already exists, such as `index.tsx`, `index.js`,
19+
`index.mts`, or `index.mjs`, fail instead of reusing it.
20+
- If `index.ts` already exists, merge into it idempotently. Do not overwrite it.
21+
- Export only symbols needed by incoming references.
22+
- Rewrite default imports to named imports using the local default import name.
23+
- For namespace imports, generate a namespace value in the barrel:
24+
25+
```ts
26+
import * as util from './util';
27+
export {util};
28+
```
29+
30+
Then rewrite the importer to `import {util} from './shared';`.
31+
- Skip string-literal dynamic imports with a warning.
32+
- Skip side-effect imports with a warning.
33+
- Do not support CommonJS `require(...)`.
34+
- Fail on public-name collisions rather than inventing aliases.
35+
- Generated paths from the barrel should prefer directory-index paths. For a
36+
target like `src/shared/sub/index.ts`, render `./sub`, not `./sub/index`.
37+
38+
## Phase 1: Fixtures And API Contract
39+
40+
Add fixtures first so the behavior is locked before implementation details get
41+
too deep.
42+
43+
1. Add `test/fixtures/barrel/named-imports-apply`.
44+
- External imports reference two files inside a directory.
45+
- Expected tree includes a new `index.ts`.
46+
- External imports point at the directory.
47+
2. Add `test/fixtures/barrel/default-import`.
48+
- Covers `import Button from './components/Button'`.
49+
- Expected import is `import {Button} from './components';`.
50+
- Expected barrel line is `export {default as Button} from './Button';`.
51+
3. Add `test/fixtures/barrel/mixed-default-and-named`.
52+
- Covers one import declaration containing default and named specifiers.
53+
4. Add `test/fixtures/barrel/type-only`.
54+
- Covers `import type`.
55+
- Expected barrel uses `export type`.
56+
5. Add `test/fixtures/barrel/namespace-import`.
57+
- Covers namespace import rewriting and generated namespace import/export in
58+
the barrel.
59+
6. Add `test/fixtures/barrel/re-export-source`.
60+
- Covers `export {x} from './shared/x'`.
61+
7. Add `test/fixtures/barrel/existing-index-idempotent`.
62+
- Existing `index.ts` gets missing generated lines appended.
63+
- A second run is a no-op or produces stable no-op output.
64+
8. Add negative fixtures:
65+
- `name-collision`
66+
- `dynamic-import-warning`
67+
- `side-effect-import-warning`
68+
- `sibling-index-conflict`
69+
- `missing-directory`
70+
9. Add a dry-run JSON fixture.
71+
- Assert planned changes and unchanged input tree.
72+
10. Add `include-ignore` coverage.
73+
74+
Expected command shape:
75+
76+
```bash
77+
dosido barrel <directory> [--include <glob>] [--ignore <glob>] [--json]
78+
```
79+
80+
Expected public API shape:
81+
82+
```ts
83+
makeBarrel({directory}, options)
84+
```
85+
86+
## Phase 2: Command Skeleton
87+
88+
Add the command entry points without the full rewrite logic yet.
89+
90+
1. Create `src/commands/barrel.ts`.
91+
2. Define result and option types:
92+
- `MakeBarrelArgs`
93+
- `MakeBarrelOptions`
94+
- `BarrelExport`
95+
- `MakeBarrelResult`
96+
3. Add `makeBarrel(...)`.
97+
4. Validate:
98+
- project root
99+
- target directory exists and is a directory
100+
- sibling `index.*` conflicts
101+
5. Return `CommandResult`-style fields:
102+
- `changes`
103+
- `messages`
104+
- optional `warnings`
105+
6. Wire the CLI in `src/cli.ts`.
106+
- Parse repeated `--include`.
107+
- Parse repeated `--ignore`.
108+
- Support `--json` via global parser.
109+
7. Export the API from `src/index.ts`.
110+
8. Update CLI help text.
111+
112+
## Phase 3: Usage Collection
113+
114+
Build a command-local AST collector that keeps source ranges and enough
115+
specifier detail to render replacements.
116+
117+
1. Scan source files with `findSourceFiles(projectRoot, include, ignore)`.
118+
2. Skip files inside the target directory.
119+
3. Parse with `parseModule(...)`.
120+
4. For each supported declaration, resolve its source with
121+
`resolveImportPath(...)`.
122+
5. Keep records only when the resolved target is inside the target directory.
123+
6. Record warnings and continue on parse errors.
124+
7. Collect these usage kinds:
125+
- named import specifier
126+
- default import specifier
127+
- namespace import specifier
128+
- named export-from specifier
129+
- export namespace specifier, if Babel syntax support is already available
130+
- dynamic import as skipped warning
131+
- side-effect import as skipped warning
132+
8. Preserve:
133+
- source file path
134+
- resolved target file path
135+
- declaration range
136+
- source literal range
137+
- import/export kind
138+
- type-only status
139+
- imported name, exported name, and local name as applicable
140+
141+
Do not use `listIncomingDeps(...)` directly for this phase; it loses the
142+
specifier-level detail required for default and namespace rewrites.
143+
144+
## Phase 4: Barrel Export Planner
145+
146+
Transform usage records into deterministic barrel additions.
147+
148+
1. Render barrel-relative source paths with a helper.
149+
- Use `getRelativeImportPath(indexFile, targetFile)`.
150+
- If the target file is an index module, strip trailing `/index`.
151+
2. Generate needed additions:
152+
- Named value: `export {name} from './file';`
153+
- Named type: `export type {Name} from './file';`
154+
- Default: `export {default as LocalName} from './file';`
155+
- Namespace: `import * as LocalName from './file';` plus `export {LocalName};`
156+
3. Track public barrel names.
157+
- Named import public name is the imported/exported name.
158+
- Default import public name is the local default name.
159+
- Namespace import public name is the local namespace name.
160+
4. Detect collisions.
161+
- Same public name from same target and same export form is okay.
162+
- Same public name from different targets is an error.
163+
- Same public name already exported by existing `index.ts` from another
164+
target is an error.
165+
5. Sort additions for stable output.
166+
- Value exports before type exports is fine, as long as it is deterministic.
167+
- Keep namespace import/export pairs adjacent or otherwise predictable.
168+
169+
## Phase 5: Import And Export Rewrites
170+
171+
Apply source edits to each outside file.
172+
173+
1. Compute the directory import specifier for each importer:
174+
175+
```ts
176+
getRelativeImportPath(importerFile, targetDirectory)
177+
```
178+
179+
2. For named-only import declarations:
180+
- Preserve specifiers and aliases.
181+
- Replace only the source literal.
182+
3. For default imports:
183+
- Convert the default specifier to a named specifier using the local name.
184+
- Preserve existing named specifiers and aliases.
185+
- Preserve `import type` where valid.
186+
4. For mixed default plus named imports:
187+
- Render one named import from the directory.
188+
5. For namespace imports:
189+
- Rewrite `import * as util from './shared/util'` to
190+
`import {util} from './shared';`.
191+
6. For named export-from declarations:
192+
- Replace the source literal.
193+
7. For skipped dynamic and side-effect imports:
194+
- Do not rewrite.
195+
- Emit warnings that include the project-relative file path and original
196+
source value.
197+
8. Use `applySourceEdits(...)` with ranges sorted through the existing helper.
198+
9. Do not write any files until every change has been planned successfully.
199+
200+
## Phase 6: Existing `index.ts` Merge
201+
202+
Make the barrel file update idempotent.
203+
204+
1. If `index.ts` does not exist, create it with generated additions.
205+
2. If `index.ts` exists:
206+
- Parse existing exports and namespace import/export pairs.
207+
- Preserve all existing content.
208+
- Append only missing generated additions.
209+
- Ensure a trailing newline.
210+
3. Treat existing exports as part of collision detection.
211+
4. If parsing the existing index fails, fail the command before any writes.
212+
213+
The first implementation can use generated line matching for duplicate
214+
detection, but it should still parse enough existing exports to detect public
215+
name conflicts.
216+
217+
## Phase 7: CLI, Docs, And Public API
218+
219+
1. Update `src/cli.ts` usage text.
220+
2. Add command branch for `barrel`.
221+
3. Update alias map only if a short alias is wanted.
222+
- No alias is required for the first version.
223+
4. Update `src/index.ts` exports.
224+
5. Update `README.md`:
225+
- command usage
226+
- behavior notes for default and namespace imports
227+
- skipped dynamic and side-effect imports
228+
6. Add a changeset if this repo expects one for user-facing command additions.
229+
230+
## Phase 8: Verification
231+
232+
Run focused tests first, then package-level checks.
233+
234+
1. `pnpm test:run`
235+
2. `pnpm build`
236+
3. `pnpm test:dist`
237+
4. `pnpm lint`
238+
239+
For fixtures that include `tsconfig.json`, use the existing `typecheck(project)`
240+
helper in the compiled CLI tests where practical.
241+
242+
## Implementation Order
243+
244+
1. Add the smallest API-level test for named imports.
245+
2. Implement enough `barrel.ts` to pass named imports.
246+
3. Add default import coverage and implement declaration rewriting.
247+
4. Add type-only and re-export coverage.
248+
5. Add namespace coverage.
249+
6. Add existing-index merge and idempotence.
250+
7. Add skipped dynamic and side-effect warning coverage.
251+
8. Add collision and sibling-index negative coverage.
252+
9. Wire compiled CLI fixtures.
253+
10. Update README and public exports.
254+
255+
This order keeps the core path working early while delaying the high-risk
256+
collision and merge details until the shape of generated output is stable.

0 commit comments

Comments
 (0)