@appartmint/tsm-scripts 0.0.1 → 0.0.3

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/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@appartmint/tsm-scripts",
3
3
  "author": "App Art Mint LLC",
4
- "version": "0.0.1",
4
+ "version": "0.0.3",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
- "description": "TypeScript Modules from App Art Mint LLC",
7
+ "description": "TypeScript Node Scripts from App Art Mint LLC",
8
8
  "keywords": [
9
9
  "typescript",
10
10
  "modules",
@@ -25,16 +25,8 @@
25
25
  "access": "public"
26
26
  },
27
27
  "files": [
28
- "dist/**/*.{js,d.ts}"
28
+ "src/**/*.{ts,d.ts}"
29
29
  ],
30
- "exports": {
31
- "./*": {
32
- "types": "./dist/*.d.ts",
33
- "import": "./dist/*.es.js",
34
- "require": "./dist/*.cjs.js",
35
- "default": "./dist/*.es.js"
36
- }
37
- },
38
30
  "scripts": {
39
31
  "version": "npm i && git add -A",
40
32
  "postversion": "git push && git push --tags && cross-replace gh release create \"v$npm_package_version\" --generate-notes",
@@ -0,0 +1,216 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import ts from 'typescript';
4
+
5
+ const rootArg = process.argv[2];
6
+ const ROOT = rootArg ? path.resolve(rootArg) : process.cwd();
7
+
8
+ type ImportGroup = 'external' | 'workspace' | 'local' | 'environment';
9
+
10
+ interface TsConfigAliasEntry {
11
+ fileNames: Set<string>;
12
+ aliasPrefixes: string[];
13
+ }
14
+
15
+ interface ParsedImport {
16
+ spec: string;
17
+ group: ImportGroup;
18
+ finalText: string;
19
+ multi: boolean;
20
+ }
21
+
22
+ function walkTsFiles(dir: string, out: string[] = []): string[] {
23
+ for (const name of fs.readdirSync(dir, { withFileTypes: true })) {
24
+ const p = path.join(dir, name.name);
25
+ if (name.isDirectory()) walkTsFiles(p, out);
26
+ else if (name.isFile() && name.name.endsWith('.ts')) out.push(p);
27
+ }
28
+ return out;
29
+ }
30
+
31
+ function getModuleSpecifier(text: string): string {
32
+ const m = /from\s+(['"])([^'"]+)\1/.exec(text);
33
+ return m ? m[2] : '';
34
+ }
35
+
36
+ function normalizePathForCompare(p: string): string {
37
+ return path.resolve(p).replace(/\\/g, '/').toLowerCase();
38
+ }
39
+
40
+ function normalizeAliasPrefix(key: string): string {
41
+ if (key === '*' || !key) return '';
42
+ if (key.endsWith('/*')) return key.slice(0, -1);
43
+ return key;
44
+ }
45
+
46
+ function discoverRootTsconfigs(rootDir: string): string[] {
47
+ return fs
48
+ .readdirSync(rootDir, { withFileTypes: true })
49
+ .filter((entry) => entry.isFile() && /^tsconfig.*\.json$/i.test(entry.name))
50
+ .map((entry) => path.join(rootDir, entry.name));
51
+ }
52
+
53
+ function loadTsConfigAliasEntries(rootDir: string): TsConfigAliasEntry[] {
54
+ const entries: TsConfigAliasEntry[] = [];
55
+ for (const configPath of discoverRootTsconfigs(rootDir)) {
56
+ const read = ts.readConfigFile(configPath, (fileName) => ts.sys.readFile(fileName));
57
+ if (read.error) continue;
58
+
59
+ const parsed = ts.parseJsonConfigFileContent(read.config, ts.sys, path.dirname(configPath), undefined, configPath);
60
+ const paths = parsed.options.paths ?? {};
61
+ const aliasPrefixes = Object.keys(paths)
62
+ .map((key) => normalizeAliasPrefix(key))
63
+ .filter((key) => key.length > 0);
64
+ if (aliasPrefixes.length === 0) continue;
65
+
66
+ entries.push({
67
+ fileNames: new Set(parsed.fileNames.map((name) => normalizePathForCompare(name))),
68
+ aliasPrefixes
69
+ });
70
+ }
71
+ return entries;
72
+ }
73
+
74
+ function resolveLocalAliasPrefixes(filePath: string, tsconfigEntries: TsConfigAliasEntry[]): string[] {
75
+ const normalizedFilePath = normalizePathForCompare(filePath);
76
+ const prefixes = new Set<string>();
77
+ for (const entry of tsconfigEntries) {
78
+ if (!entry.fileNames.has(normalizedFilePath)) continue;
79
+ for (const prefix of entry.aliasPrefixes) {
80
+ prefixes.add(prefix);
81
+ }
82
+ }
83
+ return [...prefixes];
84
+ }
85
+
86
+ function classify(spec: string, localAliasPrefixes: string[]): ImportGroup {
87
+ if (!spec) return 'external';
88
+ if (spec.includes('/environments/') || spec.includes('environments/environment') || spec.includes('.env')) return 'environment';
89
+ if (spec.startsWith('@app-art-mint/') || spec.startsWith('@appartmint/')) return 'workspace';
90
+ if (localAliasPrefixes.some((prefix) => spec === prefix || spec.startsWith(prefix))) return 'local';
91
+ if (spec.startsWith('.') || spec.startsWith('..')) return 'local';
92
+ return 'external';
93
+ }
94
+
95
+ function groupOrder(g: ImportGroup): number {
96
+ const order: Record<ImportGroup, number> = { external: 0, workspace: 1, local: 2, environment: 3 };
97
+ return order[g];
98
+ }
99
+
100
+ function isMultiLineImport(text: string): boolean {
101
+ return text.includes('\n');
102
+ }
103
+
104
+ function fileUsesSemicolon(sourceText: string, importTexts: string[]): boolean {
105
+ if (importTexts.some((t) => /;\s*$/.test(t))) return true;
106
+ const sample = sourceText.slice(0, 2000);
107
+ return /import[^;]+;\s*\n/.test(sample);
108
+ }
109
+
110
+ function normalizeImportLine(text: string): string {
111
+ let s = text.replace(/\s+/g, ' ').replace(/;+\s*$/, '').trim();
112
+ s = s.replace(/,\s*\}(?=\s+from\s+)/g, ' }');
113
+ s = s.replace(/([\w$])}(?=\s+from\s+)/g, '$1 }');
114
+ return s;
115
+ }
116
+
117
+ function ensureSemicolon(text: string, semicolon: boolean): string {
118
+ const t = text.replace(/;+\s*$/, '').trimEnd();
119
+ return semicolon ? `${t};` : t;
120
+ }
121
+
122
+ function wrapImportIfNeeded(text: string, maxLen: number, semicolon: boolean): string {
123
+ const normalized = normalizeImportLine(text);
124
+ if (normalized.length <= maxLen) return ensureSemicolon(normalized, semicolon);
125
+
126
+ const fromMatch = /^(.+?)\s+from\s+(['"][^'"]+['"])$/.exec(normalized);
127
+ if (!fromMatch) return ensureSemicolon(normalized, semicolon);
128
+
129
+ const head = fromMatch[1].trim();
130
+ const fromPart = fromMatch[2];
131
+ const m = /^(import\s+(?:type\s+)?)(\{)(.*)(\})\s*$/.exec(head);
132
+ if (!m) return ensureSemicolon(normalized, semicolon);
133
+
134
+ const prefix = m[1];
135
+ const inner = m[3];
136
+ const names = inner
137
+ .split(',')
138
+ .map((s) => s.trim())
139
+ .filter(Boolean);
140
+ if (names.length <= 1) return ensureSemicolon(normalized, semicolon);
141
+
142
+ const innerIndent = '\t';
143
+ const lines = names.map((n) => `${innerIndent}${n},`);
144
+ const body = `${prefix}{\n${lines.join('\n')}\n} from ${fromPart}`;
145
+ return ensureSemicolon(body, semicolon);
146
+ }
147
+
148
+ function sortKey(spec: string): string {
149
+ if (spec.startsWith('@')) return spec.slice(1);
150
+ return spec;
151
+ }
152
+
153
+ function compareSpec(a: string, b: string): number {
154
+ return sortKey(a).localeCompare(sortKey(b), 'en');
155
+ }
156
+
157
+
158
+ function organizeImports(sourceText: string, filePath: string, tsconfigEntries: TsConfigAliasEntry[]): string {
159
+ const sf = ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
160
+ const localAliasPrefixes = resolveLocalAliasPrefixes(filePath, tsconfigEntries);
161
+
162
+ let i = 0;
163
+ const stmts = sf.statements;
164
+ while (i < stmts.length && ts.isImportDeclaration(stmts[i])) i++;
165
+ if (i === 0) return sourceText;
166
+
167
+ const importNodes = stmts.slice(0, i);
168
+ const replaceStart = importNodes[0].getFullStart();
169
+ const replaceEnd = importNodes[importNodes.length - 1].end;
170
+
171
+ const importTexts = importNodes.map((node) => sourceText.slice(node.getStart(sf), node.end).trim());
172
+
173
+ const semicolon = fileUsesSemicolon(sourceText, importTexts);
174
+
175
+ const parsed: ParsedImport[] = importTexts.map((trimmed) => {
176
+ const spec = getModuleSpecifier(trimmed);
177
+ const group = classify(spec, localAliasPrefixes);
178
+ const wrapped = wrapImportIfNeeded(trimmed, 100, semicolon);
179
+ return { spec, group, finalText: wrapped, multi: isMultiLineImport(wrapped) };
180
+ });
181
+
182
+ parsed.sort((a, b) => {
183
+ const go = groupOrder(a.group) - groupOrder(b.group);
184
+ if (go !== 0) return go;
185
+ const cmp = compareSpec(a.spec, b.spec);
186
+ if (cmp !== 0) return cmp;
187
+ if (a.multi !== b.multi) return a.multi ? 1 : -1;
188
+ return a.finalText.localeCompare(b.finalText);
189
+ });
190
+
191
+ const newBlock = parsed.map((p) => p.finalText).join('\n');
192
+ const before = sourceText.slice(0, replaceStart);
193
+ const after = sourceText.slice(replaceEnd);
194
+
195
+ const result = before + newBlock + after;
196
+ return result;
197
+ }
198
+
199
+ let changed = 0;
200
+ const files = walkTsFiles(ROOT);
201
+ const tsconfigEntries = loadTsConfigAliasEntries(ROOT);
202
+
203
+ for (const filePath of files) {
204
+ let text = fs.readFileSync(filePath, 'utf8');
205
+ const original = text;
206
+
207
+ text = organizeImports(text, filePath, tsconfigEntries);
208
+ text = text.replace(/\n{3,}(\/\*\*)/g, '\n\n$1');
209
+
210
+ if (text !== original) {
211
+ fs.writeFileSync(filePath, text, 'utf8');
212
+ changed++;
213
+ }
214
+ }
215
+
216
+ console.log('Updated ' + changed.toString() + ' / ' + files.length.toString() + ' files.');
@@ -0,0 +1,41 @@
1
+ import fs from 'fs';
2
+
3
+ // Check if node_modules exists
4
+ if (!fs.existsSync('node_modules')) {
5
+ console.error('node_modules does not exist');
6
+ process.exit(1);
7
+ }
8
+
9
+ // Check if tinify dependency exists
10
+ if (!fs.existsSync('node_modules/tinify')) {
11
+ console.error('tinify does not exist');
12
+ process.exit(1);
13
+ }
14
+
15
+ // Check if tinify/lib/data/cacert.pem exists
16
+ const certPath = 'node_modules/tinify/lib/data/cacert.pem';
17
+ if (!fs.existsSync(certPath)) {
18
+ console.error('cacert.pem does not exist');
19
+ process.exit(1);
20
+ }
21
+
22
+ // Check if tinify/lib/tinify/Client.js exists
23
+ const clientPath = 'node_modules/tinify/lib/tinify/Client.js';
24
+ if (!fs.existsSync(clientPath)) {
25
+ console.error('Client.js does not exist');
26
+ process.exit(1);
27
+ }
28
+
29
+ // Load cacert.pem into a string value
30
+ const certData = fs.readFileSync(certPath, 'utf8');
31
+
32
+ // Replace the line: const data = fs.readFileSync(`${__dirname}/../data/cacert.pem`).toString();
33
+ // with the cacert.pem string value
34
+ let clientData = fs.readFileSync(clientPath, 'utf8');
35
+ clientData = clientData.replace(/const data = fs\.readFileSync\(`\$\{__dirname\}\/\.\.\/data\/cacert\.pem`\)\.toString\(\);/g, `const data = \`${certData}\`;`);
36
+
37
+ // Save the file
38
+ fs.writeFileSync(clientPath, clientData);
39
+
40
+ // Log success
41
+ console.log('Tinify transformed successfully! cacert.pem injected into Client.js');
@@ -1,8 +0,0 @@
1
- const e = {}, t = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2
- __proto__: null,
3
- default: e
4
- }, Symbol.toStringTag, { value: "Module" }));
5
- export {
6
- t as _,
7
- e as f
8
- };
@@ -1,2 +0,0 @@
1
- declare const _default: import('eslint/config').Config[];
2
- export default _default;