@prestack/thread-ui 0.0.2 → 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/CHANGELOG.md +376 -0
- package/dist/ui/animations.js +2 -3
- package/dist/ui/animations.ts +1 -2
- package/dist/ui/attach.js +0 -6
- package/dist/ui/attach.ts +0 -6
- package/dist/ui/compositions.d.ts +1 -1
- package/dist/ui/compositions.js +29 -44
- package/dist/ui/compositions.ts +31 -38
- package/dist/ui/generators/dev.js +2 -1
- package/dist/ui/generators/dev.ts +2 -1
- package/dist/ui/generators/elements.js +1 -1
- package/dist/ui/generators/elements.ts +1 -1
- package/dist/ui/generators/i18n.js +2 -3
- package/dist/ui/generators/i18n.ts +1 -2
- package/dist/ui/generators/icons.js +3 -12
- package/dist/ui/generators/icons.ts +3 -3
- package/dist/ui/generators/react.js +3 -2
- package/dist/ui/generators/react.ts +3 -2
- package/dist/ui/generators/routes.js +10 -14
- package/dist/ui/generators/routes.ts +10 -6
- package/dist/ui/generators/show.js +4 -12
- package/dist/ui/generators/show.ts +4 -3
- package/dist/ui/generators/styles.js +2 -3
- package/dist/ui/generators/styles.ts +1 -2
- package/dist/ui/generators/variants.js +2 -3
- package/dist/ui/generators/variants.ts +1 -2
- package/dist/ui/materials.js +2 -3
- package/dist/ui/materials.ts +1 -2
- package/dist/ui/screens.js +7 -16
- package/dist/ui/screens.ts +6 -7
- package/dist/ui/tokens.js +1 -2
- package/dist/ui/tokens.ts +1 -2
- package/package.json +2 -2
- package/dist/ui/makers/message.d.ts +0 -1
- package/dist/ui/makers/message.js +0 -230
- package/dist/ui/makers/message.ts +0 -292
|
@@ -1,15 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
-
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
-
});
|
|
6
|
-
}
|
|
7
|
-
return path;
|
|
8
|
-
};
|
|
9
|
-
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from "@prestack/thread/core/files";
|
|
2
|
+
import { valuesIn } from "@prestack/thread/core/rows";
|
|
10
3
|
import { join, resolve } from 'node:path';
|
|
11
4
|
import { argv, stdout } from 'node:process';
|
|
12
|
-
import { pathToFileURL } from 'node:url';
|
|
13
5
|
import { exit, invoked } from "@prestack/thread/core/invoked";
|
|
14
6
|
import { named as packageOf } from "@prestack/thread/core/scope";
|
|
15
7
|
import { findTree, inTree } from "@prestack/thread/core/tree";
|
|
@@ -25,7 +17,7 @@ function pascal(id) {
|
|
|
25
17
|
.join('');
|
|
26
18
|
}
|
|
27
19
|
async function node(file, name) {
|
|
28
|
-
const held = (
|
|
20
|
+
const held = valuesIn(readFileSync(inTree(findTree(), file)));
|
|
29
21
|
const one = held[name];
|
|
30
22
|
if (one === undefined)
|
|
31
23
|
return {};
|
|
@@ -37,7 +29,7 @@ async function fileFor(root, at, id) {
|
|
|
37
29
|
if (!existsSync(path) || !LOADABLE.test(path))
|
|
38
30
|
return undefined;
|
|
39
31
|
try {
|
|
40
|
-
const held = (
|
|
32
|
+
const held = valuesIn(readFileSync(path));
|
|
41
33
|
return held[exported(id)];
|
|
42
34
|
}
|
|
43
35
|
catch (wrong) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync } from '
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from '@prestack/thread/core/files'
|
|
2
|
+
import { valuesIn } from '@prestack/thread/core/rows'
|
|
2
3
|
import { join, resolve } from 'node:path'
|
|
3
4
|
import { argv, stdout } from 'node:process'
|
|
4
5
|
import { pathToFileURL } from 'node:url'
|
|
@@ -39,7 +40,7 @@ function pascal(id: string): string {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
async function node(file: string, name: string): Promise<Node> {
|
|
42
|
-
const held = (
|
|
43
|
+
const held = valuesIn(readFileSync(inTree(findTree(), file))) as Record<string, unknown>
|
|
43
44
|
const one = held[name]
|
|
44
45
|
if (one === undefined) return {}
|
|
45
46
|
return one as Node
|
|
@@ -51,7 +52,7 @@ async function fileFor(root: string, at: string, id: string): Promise<Record<str
|
|
|
51
52
|
const path = resolve(root, at)
|
|
52
53
|
if (!existsSync(path) || !LOADABLE.test(path)) return undefined
|
|
53
54
|
try {
|
|
54
|
-
const held = (
|
|
55
|
+
const held = valuesIn(readFileSync(path)) as Record<string, unknown>
|
|
55
56
|
return held[exported(id)] as Record<string, unknown> | undefined
|
|
56
57
|
} catch (wrong) {
|
|
57
58
|
|
|
@@ -6,8 +6,8 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
6
6
|
}
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
|
-
import { existsSync,
|
|
10
|
-
import {
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync } from "@prestack/thread/core/files";
|
|
10
|
+
import { join, resolve } from 'node:path';
|
|
11
11
|
import { argv, stderr, stdout } from 'node:process';
|
|
12
12
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
13
13
|
import { exit } from "@prestack/thread/core/invoked";
|
|
@@ -488,7 +488,6 @@ async function run() {
|
|
|
488
488
|
'believes the wrong one. Run `thread styles` and commit what it writes.\n');
|
|
489
489
|
return 1;
|
|
490
490
|
}
|
|
491
|
-
mkdirSync(dirname(at), { recursive: true });
|
|
492
491
|
writeFileSync(at, made, 'utf8');
|
|
493
492
|
writeFileSync(join(root, 'app/_generated/declared.ts'), wearingSource, 'utf8');
|
|
494
493
|
stdout.write(`thread: wrote app/_generated/tokens.css — ${String(Object.keys(tokens).length)} tokens, ` +
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from '@prestack/thread/core/files'
|
|
2
2
|
import { dirname, join, resolve } from 'node:path'
|
|
3
3
|
import { argv, stderr, stdout } from 'node:process'
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
@@ -653,7 +653,6 @@ async function run(): Promise<number> {
|
|
|
653
653
|
return 1
|
|
654
654
|
}
|
|
655
655
|
|
|
656
|
-
mkdirSync(dirname(at), { recursive: true })
|
|
657
656
|
writeFileSync(at, made, 'utf8')
|
|
658
657
|
writeFileSync(join(root, 'app/_generated/declared.ts'), wearingSource, 'utf8')
|
|
659
658
|
|
|
@@ -6,8 +6,8 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
6
6
|
}
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
|
-
import { existsSync,
|
|
10
|
-
import { basename,
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync } from "@prestack/thread/core/files";
|
|
10
|
+
import { basename, join, resolve } from 'node:path';
|
|
11
11
|
import { argv, stdout } from 'node:process';
|
|
12
12
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
13
13
|
import { exit, invoked } from "@prestack/thread/core/invoked";
|
|
@@ -117,7 +117,6 @@ async function run() {
|
|
|
117
117
|
}
|
|
118
118
|
else {
|
|
119
119
|
for (const seed of made) {
|
|
120
|
-
mkdirSync(dirname(join(root, seed.path)), { recursive: true });
|
|
121
120
|
writeFileSync(join(root, seed.path), seed.content, 'utf8');
|
|
122
121
|
stdout.write(`thread: seeded ${seed.path}\n`);
|
|
123
122
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from '@prestack/thread/core/files'
|
|
2
2
|
import { basename, dirname, join, resolve } from 'node:path'
|
|
3
3
|
import { argv, stdout } from 'node:process'
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
@@ -154,7 +154,6 @@ async function run(): Promise<number> {
|
|
|
154
154
|
}
|
|
155
155
|
} else {
|
|
156
156
|
for (const seed of made) {
|
|
157
|
-
mkdirSync(dirname(join(root, seed.path)), { recursive: true })
|
|
158
157
|
writeFileSync(join(root, seed.path), seed.content, 'utf8')
|
|
159
158
|
stdout.write(`thread: seeded ${seed.path}\n`)
|
|
160
159
|
}
|
package/dist/ui/materials.js
CHANGED
|
@@ -6,8 +6,8 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
6
6
|
}
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
|
-
import { existsSync,
|
|
10
|
-
import { basename,
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync } from "@prestack/thread/core/files";
|
|
10
|
+
import { basename, join } from 'node:path';
|
|
11
11
|
import { argv, stdout } from 'node:process';
|
|
12
12
|
import { pathToFileURL } from 'node:url';
|
|
13
13
|
import { exit, invoked } from "@prestack/thread/core/invoked";
|
|
@@ -130,7 +130,6 @@ async function run() {
|
|
|
130
130
|
}
|
|
131
131
|
else {
|
|
132
132
|
for (const one of made) {
|
|
133
|
-
mkdirSync(dirname(join(tree.at, one.path)), { recursive: true });
|
|
134
133
|
writeFileSync(join(tree.at, one.path), one.content, 'utf8');
|
|
135
134
|
stdout.write(`thread: seeded ${one.path}\n`);
|
|
136
135
|
}
|
package/dist/ui/materials.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from '@prestack/thread/core/files'
|
|
2
2
|
import { basename, dirname, join } from 'node:path'
|
|
3
3
|
import { argv, stdout } from 'node:process'
|
|
4
4
|
import { pathToFileURL } from 'node:url'
|
|
@@ -155,7 +155,6 @@ async function run(): Promise<number> {
|
|
|
155
155
|
}
|
|
156
156
|
} else {
|
|
157
157
|
for (const one of made) {
|
|
158
|
-
mkdirSync(dirname(join(tree.at, one.path)), { recursive: true })
|
|
159
158
|
writeFileSync(join(tree.at, one.path), one.content, 'utf8')
|
|
160
159
|
stdout.write(`thread: seeded ${one.path}\n`)
|
|
161
160
|
}
|
package/dist/ui/screens.js
CHANGED
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
-
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
-
});
|
|
6
|
-
}
|
|
7
|
-
return path;
|
|
8
|
-
};
|
|
9
|
-
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
10
|
-
import { basename, dirname, join } from 'node:path';
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "@prestack/thread/core/files";
|
|
2
|
+
import { basename, join } from 'node:path';
|
|
11
3
|
import { argv, stdout } from 'node:process';
|
|
12
|
-
import { pathToFileURL } from 'node:url';
|
|
13
4
|
import { exit, invoked } from "@prestack/thread/core/invoked";
|
|
5
|
+
import { valuesIn } from "@prestack/thread/core/rows";
|
|
14
6
|
import { findTree, inTree } from "@prestack/thread/core/tree";
|
|
15
7
|
import { HOME, settled, undeclared } from "./compositions.js";
|
|
16
8
|
import { PRESENTATIONS } from "./door.js";
|
|
@@ -81,14 +73,14 @@ export const ${name} = screen('${row.id}', {}, () => (
|
|
|
81
73
|
export function seeds(rows, held) {
|
|
82
74
|
return rows.filter((row) => !held(row.at)).map((row) => ({ path: row.at, content: starter(row) }));
|
|
83
75
|
}
|
|
84
|
-
|
|
76
|
+
function run() {
|
|
85
77
|
const tree = findTree();
|
|
86
78
|
const at = inTree(tree, 'screens.ts');
|
|
87
79
|
if (!existsSync(at)) {
|
|
88
80
|
stdout.write(`thread: this tree has no ${tree.name}/${basename(at)}, so it declares no screen and there is nothing to seed or check.\n`);
|
|
89
81
|
return 0;
|
|
90
82
|
}
|
|
91
|
-
const held = (
|
|
83
|
+
const held = valuesIn(readFileSync(at));
|
|
92
84
|
if (held.SCREEN === undefined) {
|
|
93
85
|
stdout.write(`thread: ${tree.name}/screens.ts exports no \`SCREEN\`, so nothing declares a screen here.\n`);
|
|
94
86
|
return 1;
|
|
@@ -104,12 +96,11 @@ async function run() {
|
|
|
104
96
|
}
|
|
105
97
|
if (!checking) {
|
|
106
98
|
for (const one of made) {
|
|
107
|
-
mkdirSync(dirname(join(tree.at, one.path)), { recursive: true });
|
|
108
99
|
writeFileSync(join(tree.at, one.path), one.content, 'utf8');
|
|
109
100
|
stdout.write(`thread: seeded ${one.path}\n`);
|
|
110
101
|
}
|
|
111
102
|
}
|
|
112
|
-
const loose = [...
|
|
103
|
+
const loose = [...undeclared(tree, rows), ...settled(tree.at, rows, checking, 'thread screens', new Set())];
|
|
113
104
|
for (const one of loose)
|
|
114
105
|
stdout.write(`thread: ${one}\n`);
|
|
115
106
|
stdout.write(`thread: ${String(rows.length)} screen(s) declared, ${String(made.length)} seeded, ` +
|
|
@@ -118,4 +109,4 @@ async function run() {
|
|
|
118
109
|
return loose.length === 0 ? 0 : 1;
|
|
119
110
|
}
|
|
120
111
|
if (invoked(import.meta.url))
|
|
121
|
-
exit(
|
|
112
|
+
exit(run());
|
package/dist/ui/screens.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from '@prestack/thread/core/files'
|
|
2
2
|
import { basename, dirname, join } from 'node:path'
|
|
3
3
|
import { argv, stdout } from 'node:process'
|
|
4
|
-
import { pathToFileURL } from 'node:url'
|
|
5
4
|
|
|
6
5
|
import { exit, invoked } from '@prestack/thread/core/invoked'
|
|
6
|
+
import { valuesIn } from '@prestack/thread/core/rows'
|
|
7
7
|
import type { Threaded } from '@prestack/thread/core/thread'
|
|
8
8
|
import { findTree, inTree } from '@prestack/thread/core/tree'
|
|
9
9
|
|
|
@@ -93,14 +93,14 @@ export function seeds(rows: readonly Threaded[], held: (at: string) => boolean):
|
|
|
93
93
|
return rows.filter((row) => !held(row.at)).map((row) => ({ path: row.at, content: starter(row) }))
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
function run(): number {
|
|
97
97
|
const tree = findTree()
|
|
98
98
|
const at = inTree(tree, 'screens.ts')
|
|
99
99
|
if (!existsSync(at)) {
|
|
100
100
|
stdout.write(`thread: this tree has no ${tree.name}/${basename(at)}, so it declares no screen and there is nothing to seed or check.\n`)
|
|
101
101
|
return 0
|
|
102
102
|
}
|
|
103
|
-
const held = (
|
|
103
|
+
const held = valuesIn(readFileSync(at)) as { SCREEN?: { readonly declares?: readonly Threaded[] } }
|
|
104
104
|
|
|
105
105
|
if (held.SCREEN === undefined) {
|
|
106
106
|
stdout.write(`thread: ${tree.name}/screens.ts exports no \`SCREEN\`, so nothing declares a screen here.\n`)
|
|
@@ -122,13 +122,12 @@ async function run(): Promise<number> {
|
|
|
122
122
|
|
|
123
123
|
if (!checking) {
|
|
124
124
|
for (const one of made) {
|
|
125
|
-
mkdirSync(dirname(join(tree.at, one.path)), { recursive: true })
|
|
126
125
|
writeFileSync(join(tree.at, one.path), one.content, 'utf8')
|
|
127
126
|
stdout.write(`thread: seeded ${one.path}\n`)
|
|
128
127
|
}
|
|
129
128
|
}
|
|
130
129
|
|
|
131
|
-
const loose = [...
|
|
130
|
+
const loose = [...undeclared(tree, rows), ...settled(tree.at, rows, checking, 'thread screens', new Set<string>())]
|
|
132
131
|
for (const one of loose) stdout.write(`thread: ${one}\n`)
|
|
133
132
|
|
|
134
133
|
stdout.write(
|
|
@@ -139,4 +138,4 @@ async function run(): Promise<number> {
|
|
|
139
138
|
return loose.length === 0 ? 0 : 1
|
|
140
139
|
}
|
|
141
140
|
|
|
142
|
-
if (invoked(import.meta.url)) exit(
|
|
141
|
+
if (invoked(import.meta.url)) exit(run())
|
package/dist/ui/tokens.js
CHANGED
|
@@ -6,7 +6,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
6
6
|
}
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
|
-
import { existsSync,
|
|
9
|
+
import { existsSync, readdirSync, writeFileSync } from "@prestack/thread/core/files";
|
|
10
10
|
import { basename, dirname, join } from 'node:path';
|
|
11
11
|
import { argv, stdout } from 'node:process';
|
|
12
12
|
import { pathToFileURL } from 'node:url';
|
|
@@ -169,7 +169,6 @@ async function run() {
|
|
|
169
169
|
}
|
|
170
170
|
else {
|
|
171
171
|
for (const seed of made) {
|
|
172
|
-
mkdirSync(dirname(join(tree.at, seed.path)), { recursive: true });
|
|
173
172
|
writeFileSync(join(tree.at, seed.path), seed.content, 'utf8');
|
|
174
173
|
stdout.write(`thread: seeded ${seed.path}\n`);
|
|
175
174
|
}
|
package/dist/ui/tokens.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, readdirSync, writeFileSync } from '@prestack/thread/core/files'
|
|
2
2
|
import { basename, dirname, join } from 'node:path'
|
|
3
3
|
import { argv, stdout } from 'node:process'
|
|
4
4
|
import { pathToFileURL } from 'node:url'
|
|
@@ -198,7 +198,6 @@ async function run(): Promise<number> {
|
|
|
198
198
|
}
|
|
199
199
|
} else {
|
|
200
200
|
for (const seed of made) {
|
|
201
|
-
mkdirSync(dirname(join(tree.at, seed.path)), { recursive: true })
|
|
202
201
|
writeFileSync(join(tree.at, seed.path), seed.content, 'utf8')
|
|
203
202
|
stdout.write(`thread: seeded ${seed.path}\n`)
|
|
204
203
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prestack/thread-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -17,6 +17,6 @@
|
|
|
17
17
|
"react-dom": "^19.0.0",
|
|
18
18
|
"typescript": "^6.0.0",
|
|
19
19
|
"zod": "^4.5.4",
|
|
20
|
-
"@prestack/thread": "^0.
|
|
20
|
+
"@prestack/thread": "^0.1.3"
|
|
21
21
|
}
|
|
22
22
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
-
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
-
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
-
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
-
});
|
|
6
|
-
}
|
|
7
|
-
return path;
|
|
8
|
-
};
|
|
9
|
-
import { readFileSync, writeFileSync } from 'node:fs';
|
|
10
|
-
import { join } from 'node:path';
|
|
11
|
-
import { argv, exit, stdout } from 'node:process';
|
|
12
|
-
import { pathToFileURL } from 'node:url';
|
|
13
|
-
import { ELSEWHERE, RECEIPT, answer, answering, asking, given, guidance, issue, marked, named, needed, only, prompted, refuse, spend, within, wordsIn, wrapped } from "@prestack/thread/core/asking";
|
|
14
|
-
import { named as packageOf } from "@prestack/thread/core/scope";
|
|
15
|
-
import { findTree, inTree } from "@prestack/thread/core/tree";
|
|
16
|
-
import { keyProps, keysIn, readWording, walk, wordingFile } from "../generators/i18n.js";
|
|
17
|
-
import { fileFor, languagesIn, sourceIn } from "../localizations.js";
|
|
18
|
-
const TREE = findTree();
|
|
19
|
-
const DOTTED = /^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/;
|
|
20
|
-
const KEY = { at: 'key', guide: 'lowercase words joined by dots — `checkout.confirm`, `errors.db.not_found.title`' };
|
|
21
|
-
async function languages() {
|
|
22
|
-
const path = inTree(TREE, `localizations.${'ts'}`);
|
|
23
|
-
let sheet = {};
|
|
24
|
-
try {
|
|
25
|
-
sheet = (await import(__rewriteRelativeImportExtension(pathToFileURL(path).href)));
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
sheet = {};
|
|
29
|
-
}
|
|
30
|
-
if (sheet.LOCALIZATION === undefined) {
|
|
31
|
-
refuse(`there is no ${within(TREE.at, path)} declaring a \`LOCALIZATION\` here, so this app is in no ` +
|
|
32
|
-
`language and a message has nowhere to go. Run \`npx thread attach ${packageOf('ui')}\`.`);
|
|
33
|
-
}
|
|
34
|
-
const rows = [...(sheet.LOCALIZATION.holds ?? []), ...(sheet.LOCALIZATION.declares ?? [])];
|
|
35
|
-
const held = languagesIn(rows);
|
|
36
|
-
const source = sourceIn(rows);
|
|
37
|
-
if (held.length === 0 || source === undefined) {
|
|
38
|
-
refuse('no language is declared, so there is nothing to write a message in. Add one with `npx thread create language`.');
|
|
39
|
-
}
|
|
40
|
-
return { held, source };
|
|
41
|
-
}
|
|
42
|
-
function usage(props) {
|
|
43
|
-
const found = new Map();
|
|
44
|
-
for (const file of walk(TREE.at)) {
|
|
45
|
-
const text = readFileSync(file, 'utf8');
|
|
46
|
-
for (const key of new Set(keysIn(text, props))) {
|
|
47
|
-
const held = found.get(key) ?? [];
|
|
48
|
-
held.push(within(TREE.at, file));
|
|
49
|
-
found.set(key, held);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return found;
|
|
53
|
-
}
|
|
54
|
-
const mark = (key, said) => marked([key, said]);
|
|
55
|
-
function write(at, language, held) {
|
|
56
|
-
writeFileSync(join(at, fileFor(language)), wordingFile(held), 'utf8');
|
|
57
|
-
}
|
|
58
|
-
async function run() {
|
|
59
|
-
const { held: spoken, source } = await languages();
|
|
60
|
-
const props = keyProps(TREE.at);
|
|
61
|
-
if (props.length === 0) {
|
|
62
|
-
refuse(`nothing in the installed \`${packageOf('ui')}\` declares a prop of type \`MessageKey\`, so no key could be ` +
|
|
63
|
-
'recognised anywhere. That is a broken install rather than an app with no wording.');
|
|
64
|
-
}
|
|
65
|
-
const LANGUAGES = spoken.map((one) => ({ at: one.id, guide: `what it says in ${one.name}` }));
|
|
66
|
-
const NEW = [KEY, ...LANGUAGES];
|
|
67
|
-
const wording = new Map();
|
|
68
|
-
for (const one of spoken)
|
|
69
|
-
wording.set(one.id, { ...readWording(TREE.at, one.id) });
|
|
70
|
-
const written = wording.get(source) ?? {};
|
|
71
|
-
const keys = Object.keys(written).sort();
|
|
72
|
-
const used = usage(props);
|
|
73
|
-
const blank = (key) => spoken.filter((one) => ((wording.get(one.id) ?? {})[key] ?? '') === '').map((one) => one.id);
|
|
74
|
-
const listing = [
|
|
75
|
-
`${String(keys.length)} message(s), in ${spoken.map((one) => one.id).join(', ')} — written in ${source}:`,
|
|
76
|
-
'',
|
|
77
|
-
...keys.map((key) => {
|
|
78
|
-
const missing = blank(key);
|
|
79
|
-
const said = (written[key] ?? '') || '(nothing yet)';
|
|
80
|
-
return (` [${mark(key, written[key] ?? '')}] ${key.padEnd(38)} ${said.length > 46 ? `${said.slice(0, 45)}…` : said}` +
|
|
81
|
-
`${missing.length === 0 ? '' : ` · still to write: ${missing.join(', ')}`}` +
|
|
82
|
-
`${(used.get(key) ?? []).length === 0 ? ' · named by nothing here' : ''}`);
|
|
83
|
-
}),
|
|
84
|
-
].join('\n');
|
|
85
|
-
const words = wordsIn(['new']);
|
|
86
|
-
const verb = words[0];
|
|
87
|
-
const flagged = argv.indexOf(RECEIPT);
|
|
88
|
-
if (flagged === -1) {
|
|
89
|
-
stdout.write(`${listing}\n`);
|
|
90
|
-
const nonce = issue(TREE.at, listing);
|
|
91
|
-
stdout.write([
|
|
92
|
-
'',
|
|
93
|
-
...answering('message'),
|
|
94
|
-
'',
|
|
95
|
-
...guidance(NEW),
|
|
96
|
-
'',
|
|
97
|
-
...wrapped('Every language is asked for and none may be left blank. A key that exists in one language and ' +
|
|
98
|
-
'not another is what makes a screen come out half translated, and it is refused here rather ' +
|
|
99
|
-
'than found by whoever is reading the app in the other one.', ' '),
|
|
100
|
-
'',
|
|
101
|
-
` npx thread create message new ${RECEIPT} ${nonce}`,
|
|
102
|
-
'',
|
|
103
|
-
...ELSEWHERE,
|
|
104
|
-
'',
|
|
105
|
-
...wrapped('A rename moves the wording in every language and rewrites every file in the interface that ' +
|
|
106
|
-
'names it, in one run.', ' '),
|
|
107
|
-
'',
|
|
108
|
-
].join('\n'));
|
|
109
|
-
stdout.write(`create: receipt ${nonce} — spent on use, and stale the moment the wording changes.\n`);
|
|
110
|
-
return 0;
|
|
111
|
-
}
|
|
112
|
-
spend(TREE.at, listing, argv[flagged + 1], `the wording`);
|
|
113
|
-
const all = given();
|
|
114
|
-
const { piped, ask, shut } = asking(all);
|
|
115
|
-
const ASKS = {
|
|
116
|
-
new: ['do', ...named(NEW)],
|
|
117
|
-
edit: ['do', 'mark', ...named(NEW)],
|
|
118
|
-
remove: ['do', 'mark'],
|
|
119
|
-
};
|
|
120
|
-
const doing = verb ?? (await ask('do', 'new, edit or remove: '));
|
|
121
|
-
const allowed = ASKS[doing];
|
|
122
|
-
if (allowed === undefined) {
|
|
123
|
-
shut();
|
|
124
|
-
refuse(`there is nothing called "${doing}" to do here — say new, edit, remove, or read, which needs no receipt`);
|
|
125
|
-
}
|
|
126
|
-
if (piped)
|
|
127
|
-
only(all, allowed, `\`${doing}\``);
|
|
128
|
-
if (doing === 'remove' || doing === 'edit') {
|
|
129
|
-
const which = words[1] ?? (piped ? needed(all, 'mark', `\`${doing}\``) : await ask('mark', 'mark: '));
|
|
130
|
-
const one = keys.find((key) => mark(key, written[key] ?? '') === which);
|
|
131
|
-
if (one === undefined) {
|
|
132
|
-
shut();
|
|
133
|
-
refuse(`\`${which}\` is not a mark this sheet issued — they are printed in brackets beside each message.`);
|
|
134
|
-
}
|
|
135
|
-
if (doing === 'remove') {
|
|
136
|
-
const where = used.get(one) ?? [];
|
|
137
|
-
if (where.length > 0) {
|
|
138
|
-
shut();
|
|
139
|
-
refuse(`\`${one}\` is named by ${where.slice(0, 6).join(', ')}` +
|
|
140
|
-
`${where.length > 6 ? ` and ${String(where.length - 6)} more` : ''}. ` +
|
|
141
|
-
'Removing it would leave those asking for wording that does not exist, which throws the moment ' +
|
|
142
|
-
'the screen renders. Take it out of the interface first.');
|
|
143
|
-
}
|
|
144
|
-
shut();
|
|
145
|
-
for (const language of spoken) {
|
|
146
|
-
const made = { ...(wording.get(language.id) ?? {}) };
|
|
147
|
-
delete made[one];
|
|
148
|
-
write(TREE.at, language.id, made);
|
|
149
|
-
}
|
|
150
|
-
stdout.write(`create: removed ${one} from ${String(spoken.length)} language file(s)\n`);
|
|
151
|
-
return 0;
|
|
152
|
-
}
|
|
153
|
-
const renamed = piped ? (answer(all, KEY.at) ?? '') : await ask(KEY.at, `key (${one}, blank to keep) `);
|
|
154
|
-
if (renamed !== '' && !DOTTED.test(renamed)) {
|
|
155
|
-
shut();
|
|
156
|
-
refuse(`\`${renamed}\` is not a key — lowercase words joined by dots.`);
|
|
157
|
-
}
|
|
158
|
-
if (renamed !== '' && written[renamed] !== undefined) {
|
|
159
|
-
shut();
|
|
160
|
-
refuse(`\`${renamed}\` is already a message. Two keys with one name is one key, and the other's wording is lost.`);
|
|
161
|
-
}
|
|
162
|
-
const said = new Map();
|
|
163
|
-
for (const language of spoken) {
|
|
164
|
-
const now = (wording.get(language.id) ?? {})[one] ?? '';
|
|
165
|
-
const next = piped
|
|
166
|
-
? (answer(all, language.id) ?? '')
|
|
167
|
-
: await ask(language.id, `${language.name} (${now || 'nothing yet'}) `);
|
|
168
|
-
said.set(language.id, next === '' ? now : next);
|
|
169
|
-
}
|
|
170
|
-
shut();
|
|
171
|
-
const name = renamed === '' ? one : renamed;
|
|
172
|
-
for (const language of spoken) {
|
|
173
|
-
const made = { ...(wording.get(language.id) ?? {}) };
|
|
174
|
-
delete made[one];
|
|
175
|
-
made[name] = said.get(language.id) ?? '';
|
|
176
|
-
write(TREE.at, language.id, made);
|
|
177
|
-
}
|
|
178
|
-
let moved = 0;
|
|
179
|
-
if (renamed !== '') {
|
|
180
|
-
for (const file of walk(TREE.at)) {
|
|
181
|
-
const text = readFileSync(file, 'utf8');
|
|
182
|
-
if (!keysIn(text, props).includes(one))
|
|
183
|
-
continue;
|
|
184
|
-
writeFileSync(file, text.replace(new RegExp(`(\\b(?:${props.join('|')})\\s*=\\s*")${one.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(")`, 'g'), `$1${name}$2`), 'utf8');
|
|
185
|
-
moved += 1;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
stdout.write(`create: ${one}${renamed === '' ? '' : ` is now ${name}`} — rewritten in ${String(spoken.length)} language file(s)` +
|
|
189
|
-
`${moved === 0 ? '' : ` and ${String(moved)} file(s) of the interface`}\n`);
|
|
190
|
-
const still = blankIn(said, spoken);
|
|
191
|
-
if (still.length > 0) {
|
|
192
|
-
stdout.write(`create: still nothing in ${still.join(', ')} — the gate refuses while that holds.\n`);
|
|
193
|
-
}
|
|
194
|
-
return 0;
|
|
195
|
-
}
|
|
196
|
-
const key = piped ? needed(all, KEY.at, '`new`') : await ask(KEY.at, prompted(KEY));
|
|
197
|
-
if (!DOTTED.test(key)) {
|
|
198
|
-
shut();
|
|
199
|
-
refuse(`\`${key}\` is not a key — lowercase words joined by dots, like \`checkout.confirm\`.`);
|
|
200
|
-
}
|
|
201
|
-
if (written[key] !== undefined) {
|
|
202
|
-
shut();
|
|
203
|
-
refuse(`\`${key}\` is already a message. Edit it by its mark, or choose another key.`);
|
|
204
|
-
}
|
|
205
|
-
const said = new Map();
|
|
206
|
-
for (const language of spoken) {
|
|
207
|
-
const next = piped
|
|
208
|
-
? (answer(all, language.id) ?? '')
|
|
209
|
-
: await ask(language.id, `what it says in ${language.name}: `);
|
|
210
|
-
if (next === '') {
|
|
211
|
-
shut();
|
|
212
|
-
refuse(`\`${key}\` has nothing to say in ${language.name}. Every language is answered at the moment a key ` +
|
|
213
|
-
'is made, because a key that exists in one language and not another is a screen that comes out ' +
|
|
214
|
-
'half translated. There is no way past this on purpose.');
|
|
215
|
-
}
|
|
216
|
-
said.set(language.id, next);
|
|
217
|
-
}
|
|
218
|
-
shut();
|
|
219
|
-
for (const language of spoken) {
|
|
220
|
-
write(TREE.at, language.id, { ...(wording.get(language.id) ?? {}), [key]: said.get(language.id) ?? '' });
|
|
221
|
-
}
|
|
222
|
-
stdout.write(`create: wrote ${key} in ${spoken.map((one) => one.id).join(', ')}\n`);
|
|
223
|
-
stdout.write(`create: name it in the interface with one of: ${props.join(', ')} — for example \`label="${key}"\`.\n`);
|
|
224
|
-
return 0;
|
|
225
|
-
}
|
|
226
|
-
function blankIn(said, spoken) {
|
|
227
|
-
return spoken.filter((one) => (said.get(one.id) ?? '') === '').map((one) => one.id);
|
|
228
|
-
}
|
|
229
|
-
if (argv[1] !== undefined)
|
|
230
|
-
exit(await run());
|