apotheke 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // cli.ts
4
- import path3 from "path";
5
- import fs from "fs";
6
4
  import { execFileSync } from "child_process";
5
+ import fs from "fs";
6
+ import path3 from "path";
7
7
  import fg from "fast-glob";
8
8
 
9
9
  // src/config.ts
10
- import path from "path";
11
10
  import { existsSync, readFileSync } from "fs";
11
+ import path from "path";
12
12
  function mergeConfigs(parent, child) {
13
13
  const mergedGroups = [...parent.groups];
14
14
  for (const childGroup of child.groups ?? []) {
@@ -74,65 +74,6 @@ function loadTsConfig(dir) {
74
74
  }
75
75
  }
76
76
 
77
- // src/parser.ts
78
- import { parseSync } from "oxc-parser";
79
- function parseImports(source) {
80
- const result = parseSync("file.tsx", source);
81
- const comments = result.comments;
82
- const nodes = [];
83
- for (const node of result.program.body) {
84
- if (node.type !== "ImportDeclaration") continue;
85
- const specifier = node.source.value;
86
- let defaultImport;
87
- const namedImports = [];
88
- let namespaceImport;
89
- for (const s of node.specifiers) {
90
- if (s.type === "ImportDefaultSpecifier") {
91
- defaultImport = s.local.name;
92
- } else if (s.type === "ImportNamespaceSpecifier") {
93
- namespaceImport = s.local.name;
94
- } else if (s.type === "ImportSpecifier") {
95
- const imported = s.imported;
96
- const name = imported.type === "Identifier" ? imported.name : imported.value;
97
- const localName = s.local.name;
98
- const kind = s.importKind === "type" ? "type" : "value";
99
- const named = { name, kind };
100
- if (localName !== name) named.alias = localName;
101
- namedImports.push(named);
102
- }
103
- }
104
- const importKind = node.importKind === "type" ? "type" : "value";
105
- const isSideEffect = node.specifiers.length === 0 && importKind === "value";
106
- const importNode = {
107
- specifier,
108
- namedImports,
109
- isSideEffect,
110
- importKind,
111
- start: node.start,
112
- end: node.end
113
- };
114
- if (defaultImport) importNode.defaultImport = defaultImport;
115
- if (namespaceImport) importNode.namespaceImport = namespaceImport;
116
- const attached = findAttachedComment(source, node.start, comments);
117
- if (attached) importNode.attachedComment = attached;
118
- nodes.push(importNode);
119
- }
120
- return nodes;
121
- }
122
- function findAttachedComment(source, importStart, comments) {
123
- const linesBefore = source.slice(0, importStart).split("\n");
124
- const importLine = linesBefore.length - 1;
125
- for (const comment of comments) {
126
- if (comment.type !== "Line") continue;
127
- const commentLines = source.slice(0, comment.end).split("\n");
128
- const commentLine = commentLines.length - 1;
129
- if (commentLine === importLine - 1) {
130
- return `//${comment.value}`;
131
- }
132
- }
133
- return void 0;
134
- }
135
-
136
77
  // src/deduplicator.ts
137
78
  function deduplicateImports(imports) {
138
79
  const map = /* @__PURE__ */ new Map();
@@ -161,6 +102,8 @@ function deduplicateImports(imports) {
161
102
 
162
103
  // src/grouper.ts
163
104
  import path2 from "path";
105
+ var SIDE_EFFECTS_GROUP = "SideEffects";
106
+ var OTHERS_GROUP = "Others";
164
107
  function groupImports(imports, config, options = {}) {
165
108
  const buckets = /* @__PURE__ */ new Map();
166
109
  for (const node of imports) {
@@ -169,21 +112,21 @@ function groupImports(imports, config, options = {}) {
169
112
  buckets.get(groupName).push(node);
170
113
  }
171
114
  const result = [];
172
- if (buckets.has("SideEffects")) {
173
- result.push({ name: "SideEffects", imports: buckets.get("SideEffects") });
115
+ if (buckets.has(SIDE_EFFECTS_GROUP)) {
116
+ result.push({ name: SIDE_EFFECTS_GROUP, imports: buckets.get(SIDE_EFFECTS_GROUP) });
174
117
  }
175
118
  for (const group of config.groups) {
176
119
  if (buckets.has(group.name)) {
177
120
  result.push({ name: group.name, imports: buckets.get(group.name) });
178
121
  }
179
122
  }
180
- if (buckets.has("Others")) {
181
- result.push({ name: "Others", imports: buckets.get("Others") });
123
+ if (buckets.has(OTHERS_GROUP)) {
124
+ result.push({ name: OTHERS_GROUP, imports: buckets.get(OTHERS_GROUP) });
182
125
  }
183
126
  return result;
184
127
  }
185
128
  function assignGroup(node, config, options) {
186
- if (node.isSideEffect) return "SideEffects";
129
+ if (node.isSideEffect) return SIDE_EFFECTS_GROUP;
187
130
  const canonicalPath = resolveCanonicalPath(node.specifier, config, options);
188
131
  for (const group of config.groups) {
189
132
  for (const pattern of group.match) {
@@ -191,7 +134,7 @@ function assignGroup(node, config, options) {
191
134
  if (canonicalPath && matches(canonicalPath, pattern)) return group.name;
192
135
  }
193
136
  }
194
- return "Others";
137
+ return OTHERS_GROUP;
195
138
  }
196
139
  function resolveCanonicalPath(specifier, config, options) {
197
140
  if (specifier.startsWith(".") || specifier.startsWith("/")) {
@@ -241,21 +184,63 @@ function globToRegex(pattern) {
241
184
  return new RegExp(re + "$");
242
185
  }
243
186
 
244
- // src/sorter.ts
245
- function sortGroup(imports) {
246
- return [...imports].sort((a, b) => {
247
- if (a.importKind === "type" && b.importKind !== "type") return -1;
248
- if (a.importKind !== "type" && b.importKind === "type") return 1;
249
- return a.specifier.localeCompare(b.specifier);
250
- });
187
+ // src/parser.ts
188
+ import { parseSync } from "oxc-parser";
189
+ function parseImports(source) {
190
+ const result = parseSync("file.tsx", source);
191
+ const comments = result.comments;
192
+ const nodes = [];
193
+ for (const node of result.program.body) {
194
+ if (node.type !== "ImportDeclaration") continue;
195
+ const specifier = node.source.value;
196
+ let defaultImport;
197
+ const namedImports = [];
198
+ let namespaceImport;
199
+ for (const s of node.specifiers) {
200
+ if (s.type === "ImportDefaultSpecifier") {
201
+ defaultImport = s.local.name;
202
+ } else if (s.type === "ImportNamespaceSpecifier") {
203
+ namespaceImport = s.local.name;
204
+ } else if (s.type === "ImportSpecifier") {
205
+ const imported = s.imported;
206
+ const name = imported.type === "Identifier" ? imported.name : imported.value;
207
+ const localName = s.local.name;
208
+ const kind = s.importKind === "type" ? "type" : "value";
209
+ const named = { name, kind };
210
+ if (localName !== name) named.alias = localName;
211
+ namedImports.push(named);
212
+ }
213
+ }
214
+ const importKind = node.importKind === "type" ? "type" : "value";
215
+ const isSideEffect = node.specifiers.length === 0 && importKind === "value";
216
+ const importNode = {
217
+ specifier,
218
+ namedImports,
219
+ isSideEffect,
220
+ importKind,
221
+ start: node.start,
222
+ end: node.end
223
+ };
224
+ if (defaultImport) importNode.defaultImport = defaultImport;
225
+ if (namespaceImport) importNode.namespaceImport = namespaceImport;
226
+ const attached = findAttachedComment(source, node.start, comments);
227
+ if (attached) importNode.attachedComment = attached;
228
+ nodes.push(importNode);
229
+ }
230
+ return nodes;
251
231
  }
252
- function sortNamedImports(node) {
253
- const sorted = [...node.namedImports].sort((a, b) => {
254
- if (a.kind === "type" && b.kind !== "type") return -1;
255
- if (a.kind !== "type" && b.kind === "type") return 1;
256
- return a.name.localeCompare(b.name);
257
- });
258
- return { ...node, namedImports: sorted };
232
+ function findAttachedComment(source, importStart, comments) {
233
+ const linesBefore = source.slice(0, importStart).split("\n");
234
+ const importLine = linesBefore.length - 1;
235
+ for (const comment of comments) {
236
+ if (comment.type !== "Line") continue;
237
+ const commentLines = source.slice(0, comment.end).split("\n");
238
+ const commentLine = commentLines.length - 1;
239
+ if (commentLine === importLine - 1) {
240
+ return `//${comment.value}`;
241
+ }
242
+ }
243
+ return void 0;
259
244
  }
260
245
 
261
246
  // src/printer.ts
@@ -307,6 +292,23 @@ function buildImportStatement(node, q) {
307
292
  return `import ${typePrefix}${parts.join(", ")} from ${q}${node.specifier}${q};`;
308
293
  }
309
294
 
295
+ // src/sorter.ts
296
+ function sortGroup(imports) {
297
+ return [...imports].sort((a, b) => {
298
+ if (a.importKind === "type" && b.importKind !== "type") return -1;
299
+ if (a.importKind !== "type" && b.importKind === "type") return 1;
300
+ return a.specifier.localeCompare(b.specifier);
301
+ });
302
+ }
303
+ function sortNamedImports(node) {
304
+ const sorted = [...node.namedImports].sort((a, b) => {
305
+ if (a.kind === "type" && b.kind !== "type") return -1;
306
+ if (a.kind !== "type" && b.kind === "type") return 1;
307
+ return a.name.localeCompare(b.name);
308
+ });
309
+ return { ...node, namedImports: sorted };
310
+ }
311
+
310
312
  // src/format.ts
311
313
  function collectOrphanSegments(source, imports) {
312
314
  const orphans = [];
@@ -335,7 +337,9 @@ function formatImports(source, config, options = {}) {
335
337
  const deduped = deduplicateImports(clean);
336
338
  const sorted = deduped.map(sortNamedImports);
337
339
  const grouped = groupImports(sorted, config, options);
338
- const sortedGroups = grouped.map((g) => ({ ...g, imports: sortGroup(g.imports) }));
340
+ const sortedGroups = grouped.map(
341
+ (g) => g.name === SIDE_EFFECTS_GROUP ? g : { ...g, imports: sortGroup(g.imports) }
342
+ );
339
343
  const q = detectQuoteChar(source);
340
344
  const newImportBlock = printGroups(sortedGroups, config, q);
341
345
  const firstImport = imports[0];
@@ -497,19 +501,21 @@ async function expandGlobs(patterns) {
497
501
  }
498
502
  function getSubmoduleDirs(cwd) {
499
503
  try {
500
- const output = execFileSync("git", ["-C", cwd, "submodule", "foreach", "--recursive", "--quiet", "pwd"], {
501
- encoding: "utf-8",
502
- stdio: ["ignore", "pipe", "ignore"]
503
- });
504
+ const output = execFileSync(
505
+ "git",
506
+ ["-C", cwd, "submodule", "foreach", "--recursive", "--quiet", "pwd"],
507
+ {
508
+ encoding: "utf-8",
509
+ stdio: ["ignore", "pipe", "ignore"]
510
+ }
511
+ );
504
512
  return output.trim().split("\n").filter(Boolean);
505
513
  } catch {
506
514
  return [];
507
515
  }
508
516
  }
509
517
  function isUnderSubmodule(filePath, submoduleDirs) {
510
- return submoduleDirs.some(
511
- (dir) => filePath === dir || filePath.startsWith(dir + path3.sep)
512
- );
518
+ return submoduleDirs.some((dir) => filePath === dir || filePath.startsWith(dir + path3.sep));
513
519
  }
514
520
  function printDiff(file, original, updated) {
515
521
  const origLines = original.split("\n");
package/dist/index.d.ts CHANGED
@@ -7,7 +7,6 @@ interface ApothekeConfig {
7
7
  groups: GroupConfig[];
8
8
  aliases?: Record<string, string>;
9
9
  baseUrl?: string;
10
- normalizeImports?: 'alias' | 'relative' | 'absolute' | false;
11
10
  groupSeparator?: boolean;
12
11
  groupComments?: boolean;
13
12
  }
@@ -24,7 +23,7 @@ declare function formatImports(source: string, config: ApothekeConfig, options?:
24
23
  * Prettier plugin usage (.prettierrc):
25
24
  * { "plugins": ["apotheke"] }
26
25
  *
27
- * Requirements: prettier v3+, Bun runtime
26
+ * Requirements: prettier v3+, Node.js >= 18
28
27
  *
29
28
  * The plugin runs as a `preprocess` step, so apotheke organises imports
30
29
  * first and prettier formats the result — correct order, one pass.
@@ -32,9 +31,11 @@ declare function formatImports(source: string, config: ApothekeConfig, options?:
32
31
  * Pre-commit (lint-staged, prettier v2):
33
32
  * { "*.{ts,tsx}": ["apotheke --write", "prettier --write"] }
34
33
  */
34
+ declare function isDocumentSnippet(parentParser: unknown): boolean;
35
35
  type PrettierParser = {
36
36
  preprocess?: (text: string, opts: {
37
37
  filepath?: string;
38
+ parentParser?: string;
38
39
  }) => string | Promise<string>;
39
40
  parse?: (text: string, options: unknown) => unknown;
40
41
  astFormat?: string;
@@ -45,8 +46,8 @@ type PrettierParser = {
45
46
  declare const parsers: {
46
47
  typescript: PrettierParser;
47
48
  babel: PrettierParser;
48
- "babel-ts": PrettierParser;
49
- "babel-flow": PrettierParser;
49
+ 'babel-ts': PrettierParser;
50
+ 'babel-flow': PrettierParser;
50
51
  };
51
52
 
52
- export { type ApothekeConfig, formatImports, parsers };
53
+ export { type ApothekeConfig, formatImports, isDocumentSnippet, parsers };