@lokascript/framework 4.5.0 → 4.7.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/CHANGELOG.md +220 -8
- package/dist/api/create-dsl.d.ts +59 -1
- package/dist/api/create-dsl.d.ts.map +1 -1
- package/dist/api/domain-registry.d.ts.map +1 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +1938 -724
- package/dist/api/index.js.map +1 -1
- package/dist/api/reading.d.ts +8 -0
- package/dist/api/reading.d.ts.map +1 -0
- package/dist/api/translation-loss.d.ts +84 -0
- package/dist/api/translation-loss.d.ts.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1047 -684
- package/dist/core/index.js.map +1 -1
- package/dist/core/matching/confidence-model.d.ts +54 -0
- package/dist/core/matching/confidence-model.d.ts.map +1 -0
- package/dist/core/matching/index.d.ts +12 -0
- package/dist/core/matching/index.d.ts.map +1 -0
- package/dist/core/matching/match-skeleton.d.ts +114 -0
- package/dist/core/matching/match-skeleton.d.ts.map +1 -0
- package/dist/core/matching/stock-confidence.d.ts +15 -0
- package/dist/core/matching/stock-confidence.d.ts.map +1 -0
- package/dist/core/matching/stock-reader.d.ts +56 -0
- package/dist/core/matching/stock-reader.d.ts.map +1 -0
- package/dist/core/matching/stock-reading.d.ts +44 -0
- package/dist/core/matching/stock-reading.d.ts.map +1 -0
- package/dist/core/matching/value-reader.d.ts +62 -0
- package/dist/core/matching/value-reader.d.ts.map +1 -0
- package/dist/core/pattern-matching/index.js +561 -660
- package/dist/core/pattern-matching/index.js.map +1 -1
- package/dist/core/pattern-matching/pattern-matcher.d.ts +0 -117
- package/dist/core/pattern-matching/pattern-matcher.d.ts.map +1 -1
- package/dist/core/types.d.ts +48 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/generation/index.d.ts +2 -0
- package/dist/generation/index.d.ts.map +1 -1
- package/dist/generation/index.js +583 -1
- package/dist/generation/index.js.map +1 -1
- package/dist/generation/pattern-render.d.ts +72 -0
- package/dist/generation/pattern-render.d.ts.map +1 -0
- package/dist/generation/schema-patterns.d.ts +152 -0
- package/dist/generation/schema-patterns.d.ts.map +1 -0
- package/dist/index.cjs +2132 -890
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2110 -887
- package/dist/index.js.map +1 -1
- package/dist/ir/index.js.map +1 -1
- package/dist/parsing/index.js +561 -660
- package/dist/parsing/index.js.map +1 -1
- package/dist/testing/gate/index.d.ts +8 -0
- package/dist/testing/gate/index.d.ts.map +1 -0
- package/dist/testing/gate/index.js +632 -0
- package/dist/testing/gate/index.js.map +1 -0
- package/dist/testing/gate/lint/format.d.ts +7 -0
- package/dist/testing/gate/lint/format.d.ts.map +1 -0
- package/dist/testing/gate/lint/index.d.ts +30 -0
- package/dist/testing/gate/lint/index.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/extractor-coverage.d.ts +18 -0
- package/dist/testing/gate/lint/rules/extractor-coverage.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/index.d.ts +14 -0
- package/dist/testing/gate/lint/rules/index.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/keyword-classification.d.ts +19 -0
- package/dist/testing/gate/lint/rules/keyword-classification.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/keyword-coverage.d.ts +13 -0
- package/dist/testing/gate/lint/rules/keyword-coverage.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/marker-tokenization.d.ts +20 -0
- package/dist/testing/gate/lint/rules/marker-tokenization.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/position-ordering.d.ts +25 -0
- package/dist/testing/gate/lint/rules/position-ordering.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/renderer-coherence.d.ts +24 -0
- package/dist/testing/gate/lint/rules/renderer-coherence.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/schema-rules.d.ts +10 -0
- package/dist/testing/gate/lint/rules/schema-rules.d.ts.map +1 -0
- package/dist/testing/gate/lint/types.d.ts +74 -0
- package/dist/testing/gate/lint/types.d.ts.map +1 -0
- package/dist/testing/gate/round-trip.d.ts +163 -0
- package/dist/testing/gate/round-trip.d.ts.map +1 -0
- package/package.json +7 -3
- package/src/__test__/fixtures/sql-dsl.ts +11 -2
- package/src/__test__/sql-integration.test.ts +2 -2
- package/src/api/create-dsl-engine.test.ts +86 -0
- package/src/api/create-dsl.test.ts +120 -0
- package/src/api/create-dsl.ts +314 -39
- package/src/api/domain-registry.test.ts +70 -5
- package/src/api/domain-registry.ts +82 -1
- package/src/api/index.ts +1 -0
- package/src/api/reading.ts +29 -0
- package/src/api/translation-loss.test.ts +89 -0
- package/src/api/translation-loss.ts +128 -0
- package/src/core/index.ts +1 -0
- package/src/core/matching/confidence-model.ts +58 -0
- package/src/core/matching/index.ts +12 -0
- package/src/core/matching/match-skeleton.test.ts +179 -0
- package/src/core/matching/match-skeleton.ts +621 -0
- package/src/core/matching/stock-confidence.ts +238 -0
- package/src/core/matching/stock-reader.test.ts +181 -0
- package/src/core/matching/stock-reader.ts +186 -0
- package/src/core/matching/stock-reading.ts +681 -0
- package/src/core/matching/value-reader.ts +85 -0
- package/src/core/pattern-matching/pattern-matcher.ts +11 -876
- package/src/core/types.ts +48 -0
- package/src/generation/index.ts +2 -0
- package/src/generation/pattern-render.ts +378 -0
- package/src/generation/schema-patterns.test.ts +99 -0
- package/src/generation/schema-patterns.ts +962 -0
- package/src/testing/gate/index.ts +8 -0
- package/src/testing/gate/lint/format.ts +23 -0
- package/src/testing/gate/lint/index.ts +73 -0
- package/src/testing/gate/lint/lint.test.ts +131 -0
- package/src/testing/gate/lint/rules/extractor-coverage.ts +63 -0
- package/src/testing/gate/lint/rules/index.ts +37 -0
- package/src/testing/gate/lint/rules/keyword-classification.ts +52 -0
- package/src/testing/gate/lint/rules/keyword-coverage.ts +38 -0
- package/src/testing/gate/lint/rules/marker-tokenization.ts +129 -0
- package/src/testing/gate/lint/rules/position-ordering.ts +87 -0
- package/src/testing/gate/lint/rules/renderer-coherence.ts +100 -0
- package/src/testing/gate/lint/rules/schema-rules.ts +35 -0
- package/src/testing/gate/lint/types.ts +83 -0
- package/src/testing/gate/round-trip.test.ts +118 -0
- package/src/testing/gate/round-trip.ts +438 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lokascript/framework",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"description": "Generic framework for building multilingual DSLs with semantic parsing and grammar transformation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -56,6 +56,10 @@
|
|
|
56
56
|
"types": "./dist/testing/index.d.ts",
|
|
57
57
|
"import": "./dist/testing/index.js"
|
|
58
58
|
},
|
|
59
|
+
"./testing/gate": {
|
|
60
|
+
"types": "./dist/testing/gate/index.d.ts",
|
|
61
|
+
"import": "./dist/testing/gate/index.js"
|
|
62
|
+
},
|
|
59
63
|
"./ir": {
|
|
60
64
|
"types": "./dist/ir/index.d.ts",
|
|
61
65
|
"import": "./dist/ir/index.js"
|
|
@@ -96,10 +100,10 @@
|
|
|
96
100
|
"author": "LokaScript Contributors",
|
|
97
101
|
"license": "MIT",
|
|
98
102
|
"dependencies": {
|
|
99
|
-
"@lokascript/intent": "^4.
|
|
103
|
+
"@lokascript/intent": "^4.7.0"
|
|
100
104
|
},
|
|
101
105
|
"devDependencies": {
|
|
102
|
-
"@lokascript/domains": "^
|
|
106
|
+
"@lokascript/domains": "^4.0.0",
|
|
103
107
|
"@types/node": "^26.6.3",
|
|
104
108
|
"@vitest/coverage-v8": "^5.0.0",
|
|
105
109
|
"tsup": "^8.0.0",
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
getDefaultExtractors,
|
|
14
14
|
type SemanticNode,
|
|
15
15
|
type MultilingualDSL,
|
|
16
|
+
type DSLConfig,
|
|
16
17
|
} from '../../index';
|
|
17
18
|
|
|
18
19
|
// =============================================================================
|
|
@@ -47,6 +48,9 @@ export const selectCommand = defineCommand({
|
|
|
47
48
|
description: 'WHERE clause condition',
|
|
48
49
|
required: false,
|
|
49
50
|
expectedTypes: ['expression'],
|
|
51
|
+
// As in domain-sql: the condition is the rest of the input (`age > 18`). Without it the
|
|
52
|
+
// condition read `age` and dropped `> 18`, which parse accepted until it refused unread input.
|
|
53
|
+
greedy: true,
|
|
50
54
|
svoPosition: 0,
|
|
51
55
|
sovPosition: 0,
|
|
52
56
|
markerOverride: { en: 'where', es: 'donde' },
|
|
@@ -165,7 +169,12 @@ function generateSQL(node: SemanticNode): string {
|
|
|
165
169
|
// =============================================================================
|
|
166
170
|
|
|
167
171
|
export function createSQLDSL(): MultilingualDSL {
|
|
168
|
-
return createMultilingualDSL(
|
|
172
|
+
return createMultilingualDSL(sqlDSLConfig());
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** The fixture's config, for a test that builds the DSL with another factory. */
|
|
176
|
+
export function sqlDSLConfig(): DSLConfig {
|
|
177
|
+
return {
|
|
169
178
|
name: 'SQL DSL Test Fixture',
|
|
170
179
|
schemas: [selectCommand],
|
|
171
180
|
languages: [
|
|
@@ -187,7 +196,7 @@ export function createSQLDSL(): MultilingualDSL {
|
|
|
187
196
|
codeGenerator: {
|
|
188
197
|
generate: generateSQL,
|
|
189
198
|
},
|
|
190
|
-
}
|
|
199
|
+
};
|
|
191
200
|
}
|
|
192
201
|
|
|
193
202
|
// =============================================================================
|
|
@@ -46,8 +46,8 @@ describe('SQL DSL Integration', () => {
|
|
|
46
46
|
const condition = result.roles.get('condition');
|
|
47
47
|
expect(condition).toBeDefined();
|
|
48
48
|
if (condition && 'raw' in condition) {
|
|
49
|
-
// The condition role captures the expression after WHERE
|
|
50
|
-
expect(condition.raw).
|
|
49
|
+
// The condition role captures the whole expression after WHERE
|
|
50
|
+
expect(condition.raw).toBe('age > 18');
|
|
51
51
|
}
|
|
52
52
|
});
|
|
53
53
|
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `engine: 'core'` (domains rethink Phase 4.7): the same config and DSL as the
|
|
3
|
+
* classic engine, read by the core. framework's SQL fixture reads, compiles and
|
|
4
|
+
* translates as it does on the classic engine, refuses what it does not read,
|
|
5
|
+
* and renders through its own patterns where no renderer is configured.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
8
|
+
import { createMultilingualDSL, UnreadInputError, type DSLConfig } from './create-dsl';
|
|
9
|
+
import { sqlDSLConfig, testQueries, expectedSQL } from '../__test__/fixtures/sql-dsl';
|
|
10
|
+
import type { SemanticNode } from '../core/types';
|
|
11
|
+
import { generatePattern } from '../generation/pattern-generator';
|
|
12
|
+
import { createSchemaPatternGenerator } from '../generation/schema-patterns';
|
|
13
|
+
|
|
14
|
+
// Pass-through spies. On the SQL fixture the two engines read, compile and render alike, so
|
|
15
|
+
// these are what show which engine a DSL was built on.
|
|
16
|
+
vi.mock('../generation/pattern-generator', async importOriginal => {
|
|
17
|
+
const actual = await importOriginal<typeof import('../generation/pattern-generator')>();
|
|
18
|
+
return { ...actual, generatePattern: vi.fn(actual.generatePattern) };
|
|
19
|
+
});
|
|
20
|
+
vi.mock('../generation/schema-patterns', async importOriginal => {
|
|
21
|
+
const actual = await importOriginal<typeof import('../generation/schema-patterns')>();
|
|
22
|
+
return { ...actual, createSchemaPatternGenerator: vi.fn(actual.createSchemaPatternGenerator) };
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const core = createMultilingualDSL({ ...sqlDSLConfig(), engine: 'core' });
|
|
26
|
+
const classic = createMultilingualDSL(sqlDSLConfig());
|
|
27
|
+
|
|
28
|
+
const reading = (node: SemanticNode) =>
|
|
29
|
+
[
|
|
30
|
+
node.action,
|
|
31
|
+
...[...node.roles].map(
|
|
32
|
+
([role, v]) =>
|
|
33
|
+
`${role}=${String((v as { value?: unknown; raw?: unknown }).value ?? (v as { raw?: unknown }).raw)}`
|
|
34
|
+
),
|
|
35
|
+
].join(' ');
|
|
36
|
+
|
|
37
|
+
describe("createMultilingualDSL with engine: 'core'", () => {
|
|
38
|
+
const queries = [
|
|
39
|
+
['en', testQueries.english.simple, expectedSQL.simple],
|
|
40
|
+
['en', testQueries.english.withWhere, expectedSQL.withWhere],
|
|
41
|
+
['es', testQueries.spanish.simple, 'SELECT nombre FROM usuarios'],
|
|
42
|
+
['es', testQueries.spanish.withWhere, 'SELECT nombre FROM usuarios WHERE edad > 18'],
|
|
43
|
+
] as const;
|
|
44
|
+
|
|
45
|
+
it.each(queries)('reads and compiles %s `%s` as the classic engine does', (lang, input, sql) => {
|
|
46
|
+
expect(reading(core.parse(input, lang))).toBe(reading(classic.parse(input, lang)));
|
|
47
|
+
expect(core.compile(input, lang).code).toBe(sql);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it.each(queries)('translates %s `%s` as the classic engine does', (lang, input) => {
|
|
51
|
+
const to = lang === 'en' ? 'es' : 'en';
|
|
52
|
+
expect(core.translate(input, lang, to)).toBe(classic.translate(input, lang, to));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('refuses input it does not read', () => {
|
|
56
|
+
expect(() => core.parse('select name from users banana', 'en')).toThrow(UnreadInputError);
|
|
57
|
+
expect(core.validate('select name from users banana', 'en')).toMatchObject({
|
|
58
|
+
valid: false,
|
|
59
|
+
errorCode: 'unread-input',
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('renders through its patterns, in each language, what it reads back', () => {
|
|
64
|
+
const node = core.parse(testQueries.english.withWhere, 'en');
|
|
65
|
+
expect(core.render?.(node, 'en')).toBe('select name from users where age > 18');
|
|
66
|
+
expect(core.render?.(node, 'es')).toBe('seleccionar name de users donde age > 18');
|
|
67
|
+
expect(reading(core.parse(core.render!(node, 'es')!, 'es'))).toBe(reading(node));
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it.each([
|
|
71
|
+
['core', { engine: 'core' }],
|
|
72
|
+
['classic', { engine: 'classic' }],
|
|
73
|
+
['classic', {}],
|
|
74
|
+
] as const)('builds on the %s engine with %o', (expected, option) => {
|
|
75
|
+
vi.mocked(generatePattern).mockClear();
|
|
76
|
+
vi.mocked(createSchemaPatternGenerator).mockClear();
|
|
77
|
+
createMultilingualDSL({ ...sqlDSLConfig(), ...option }).parse(testQueries.english.simple, 'en');
|
|
78
|
+
expect(vi.mocked(createSchemaPatternGenerator).mock.calls.length > 0).toBe(expected === 'core');
|
|
79
|
+
expect(vi.mocked(generatePattern).mock.calls.length > 0).toBe(expected === 'classic');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('throws on an engine it does not know, rather than reading with the classic one', () => {
|
|
83
|
+
const misspelled = { ...sqlDSLConfig(), engine: 'cor' } as unknown as DSLConfig;
|
|
84
|
+
expect(() => createMultilingualDSL(misspelled)).toThrow('Unknown DSL engine "cor"');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
defineRole,
|
|
14
14
|
BaseTokenizer,
|
|
15
15
|
getDefaultExtractors,
|
|
16
|
+
UnreadInputError,
|
|
17
|
+
TranslationReadBackError,
|
|
16
18
|
type SemanticNode,
|
|
17
19
|
} from '../index';
|
|
18
20
|
|
|
@@ -156,6 +158,35 @@ function createBilingualTestDSL() {
|
|
|
156
158
|
});
|
|
157
159
|
}
|
|
158
160
|
|
|
161
|
+
/** The bilingual DSL with a custom renderer (and, optionally, a code generator). */
|
|
162
|
+
function createBilingualTestDSLWith(
|
|
163
|
+
renderer: (node: SemanticNode, language: string) => string | null,
|
|
164
|
+
codeGenerator?: { generate(node: SemanticNode): string }
|
|
165
|
+
) {
|
|
166
|
+
return createMultilingualDSL({
|
|
167
|
+
name: 'TestSQL-rendered',
|
|
168
|
+
schemas: [selectSchema, insertSchema],
|
|
169
|
+
languages: [
|
|
170
|
+
{
|
|
171
|
+
code: 'en',
|
|
172
|
+
name: 'English',
|
|
173
|
+
nativeName: 'English',
|
|
174
|
+
tokenizer: new EnglishTestTokenizer(),
|
|
175
|
+
patternProfile: englishProfile,
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
code: 'es',
|
|
179
|
+
name: 'Spanish',
|
|
180
|
+
nativeName: 'Español',
|
|
181
|
+
tokenizer: new SpanishTestTokenizer(),
|
|
182
|
+
patternProfile: spanishProfile,
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
renderer,
|
|
186
|
+
...(codeGenerator ? { codeGenerator } : {}),
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
159
190
|
function createTestDSL() {
|
|
160
191
|
return createMultilingualDSL({
|
|
161
192
|
name: 'TestSQL',
|
|
@@ -396,4 +427,93 @@ describe('MultilingualDSL: Explicit Syntax Support', () => {
|
|
|
396
427
|
expect(result.code).toContain('SELECT');
|
|
397
428
|
});
|
|
398
429
|
});
|
|
430
|
+
|
|
431
|
+
// ---------------------------------------------------------------------------
|
|
432
|
+
// Input left unread (DOMAINS_RETHINK.md, Phase 3)
|
|
433
|
+
// ---------------------------------------------------------------------------
|
|
434
|
+
|
|
435
|
+
describe('input left unread', () => {
|
|
436
|
+
it('refuses a parse that reads the command only by dropping what follows it', () => {
|
|
437
|
+
const dsl = createTestDSL();
|
|
438
|
+
let error: unknown;
|
|
439
|
+
try {
|
|
440
|
+
dsl.parse('select name from users banana', 'en');
|
|
441
|
+
} catch (e) {
|
|
442
|
+
error = e;
|
|
443
|
+
}
|
|
444
|
+
expect(error).toBeInstanceOf(UnreadInputError);
|
|
445
|
+
const refusal = error as UnreadInputError;
|
|
446
|
+
expect(refusal.code).toBe('unread-input');
|
|
447
|
+
expect(refusal.unread).toBe('banana');
|
|
448
|
+
expect(refusal.reading.action).toBe('select');
|
|
449
|
+
expect(refusal.message).toMatch(/leaving "banana" unread/);
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it('compile and validate report the refusal instead of compiling the shorter reading', () => {
|
|
453
|
+
const dsl = createTestDSL();
|
|
454
|
+
const compiled = dsl.compile('select name from users banana banana', 'en');
|
|
455
|
+
expect(compiled.ok).toBe(false);
|
|
456
|
+
expect(compiled.errors?.[0]).toMatch(/leaving "banana banana" unread/);
|
|
457
|
+
expect(dsl.validate('select name from users banana', 'en').valid).toBe(false);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it('translate refuses too, rather than translating the shorter reading', () => {
|
|
461
|
+
const dsl = createBilingualTestDSL();
|
|
462
|
+
expect(() => dsl.translate('select name from users banana', 'en', 'es')).toThrow(
|
|
463
|
+
UnreadInputError
|
|
464
|
+
);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('a translation that does not read back is refused, unless { lossy: "allow" }', () => {
|
|
468
|
+
// A renderer that drops the required source: the Spanish does not parse at all.
|
|
469
|
+
const dropsSource = createBilingualTestDSLWith(node =>
|
|
470
|
+
node.action === 'select' ? 'seleccionar nombre' : null
|
|
471
|
+
);
|
|
472
|
+
let error: unknown;
|
|
473
|
+
try {
|
|
474
|
+
dropsSource.translate('select nombre from users', 'en', 'es');
|
|
475
|
+
} catch (e) {
|
|
476
|
+
error = e;
|
|
477
|
+
}
|
|
478
|
+
expect(error).toBeInstanceOf(TranslationReadBackError);
|
|
479
|
+
const refusal = error as TranslationReadBackError;
|
|
480
|
+
expect(refusal.code).toBe('translation-does-not-read-back');
|
|
481
|
+
expect(refusal.partial).toBe('seleccionar nombre');
|
|
482
|
+
expect(refusal.reading).toBeNull();
|
|
483
|
+
expect(
|
|
484
|
+
dropsSource.translate('select nombre from users', 'en', 'es', { lossy: 'allow' })
|
|
485
|
+
).toBe('seleccionar nombre');
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it('a translation that reads with a value changed is refused, naming the role', () => {
|
|
489
|
+
const changesSource = createBilingualTestDSLWith(node =>
|
|
490
|
+
node.action === 'select' ? 'seleccionar nombre de clientes' : null
|
|
491
|
+
);
|
|
492
|
+
expect(() => changesSource.translate('select nombre from users', 'en', 'es')).toThrow(
|
|
493
|
+
/source: users → clientes/
|
|
494
|
+
);
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it('a difference the code generator normalizes still reads back', () => {
|
|
498
|
+
// Upper case in the render, lower case in the generator: the nodes differ, the code does not.
|
|
499
|
+
const shouts = createBilingualTestDSLWith(
|
|
500
|
+
node => (node.action === 'select' ? 'seleccionar NOMBRE de USERS' : null),
|
|
501
|
+
{
|
|
502
|
+
generate: node =>
|
|
503
|
+
`${node.action} ${[...node.roles.values()]
|
|
504
|
+
.map(v => String((v as { raw?: unknown }).raw ?? '').toLowerCase())
|
|
505
|
+
.join(' ')}`,
|
|
506
|
+
}
|
|
507
|
+
);
|
|
508
|
+
expect(shouts.translate('select nombre from users', 'en', 'es')).toBe(
|
|
509
|
+
'seleccionar NOMBRE de USERS'
|
|
510
|
+
);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it('input read whole still parses, and nothing matching at all is still "No pattern matched"', () => {
|
|
514
|
+
const dsl = createTestDSL();
|
|
515
|
+
expect(dsl.parse('select name from users', 'en').action).toBe('select');
|
|
516
|
+
expect(() => dsl.parse('banana', 'en')).toThrow(/No pattern matched/);
|
|
517
|
+
});
|
|
518
|
+
});
|
|
399
519
|
});
|