glotstack 0.0.3 → 0.0.6
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.d.ts +1 -0
- package/dist/cli.js +176 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +13 -7
- package/dist/index.js +74 -80
- package/dist/index.js.map +1 -1
- package/dist/util/findConfig.d.ts +6 -0
- package/dist/util/findConfig.js +50 -0
- package/dist/util/findConfig.js.map +1 -0
- package/dist/util/object.js +1 -1
- package/dist/util/object.js.map +1 -1
- package/dist/util/yaml.d.ts +1 -0
- package/dist/util/yaml.js +18 -0
- package/dist/util/yaml.js.map +1 -0
- package/eslint-raw-string.mjs +11 -0
- package/package-lock.json +4356 -0
- package/package.json +17 -8
- package/scripts/fix-shebang.sh +28 -0
- package/src/cli.tsx +151 -0
- package/src/index.tsx +83 -91
- package/src/util/findConfig.ts +26 -0
- package/src/util/object.ts +1 -2
- package/src/util/yaml.ts +11 -0
- package/tsconfig.json +1 -2
package/package.json
CHANGED
|
@@ -1,28 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glotstack",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"author": "JD Cumpson",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"private": false,
|
|
9
|
-
"dependencies": {
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"commander": "^13.1.0",
|
|
11
|
+
"eslint": "^9.26.0"
|
|
12
|
+
},
|
|
10
13
|
"devDependencies": {
|
|
14
|
+
"@eslint/js": "^9.26.0",
|
|
15
|
+
"@types/js-yaml": "^4.0.9",
|
|
16
|
+
"@types/node": "^22.15.17",
|
|
17
|
+
"@types/react": "^19.1.4",
|
|
18
|
+
"globals": "^16.1.0",
|
|
19
|
+
"js-yaml": "^4.1.0",
|
|
11
20
|
"typescript": "5.4.4",
|
|
12
|
-
"typescript-eslint": "^8.
|
|
21
|
+
"typescript-eslint": "^8.32.1"
|
|
13
22
|
},
|
|
14
23
|
"peerDependencies": {
|
|
15
|
-
"
|
|
16
|
-
"react": "^18.3.1"
|
|
24
|
+
"react": "^19.1.0"
|
|
17
25
|
},
|
|
18
26
|
"resolutions": {
|
|
19
27
|
"react": "18.3.1"
|
|
20
28
|
},
|
|
21
29
|
"bin": {
|
|
22
|
-
"glotstack": "dist/
|
|
30
|
+
"glotstack": "dist/cli.js"
|
|
23
31
|
},
|
|
24
32
|
"scripts": {
|
|
25
|
-
"build": "tsc",
|
|
26
|
-
"prepublishOnly": "yarn run build"
|
|
33
|
+
"build": "tsc && scripts/fix-shebang.sh dist/cli.js",
|
|
34
|
+
"prepublishOnly": "yarn run build",
|
|
35
|
+
"glotstack": "node dist/cli.js"
|
|
27
36
|
}
|
|
28
37
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
FILE="$1"
|
|
4
|
+
SHEBANG='#!/usr/bin/env node'
|
|
5
|
+
|
|
6
|
+
if [ -z "$FILE" ]; then
|
|
7
|
+
echo "Usage: $0 <file>"
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
if [ ! -f "$FILE" ]; then
|
|
12
|
+
echo "File not found: $FILE"
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
FIRST_LINE=$(head -n 1 "$FILE")
|
|
17
|
+
|
|
18
|
+
if [ "$FIRST_LINE" != "$SHEBANG" ]; then
|
|
19
|
+
echo "Injecting shebang into $FILE"
|
|
20
|
+
TMP_FILE=$(mktemp)
|
|
21
|
+
echo "$SHEBANG" > "$TMP_FILE"
|
|
22
|
+
cat "$FILE" >> "$TMP_FILE"
|
|
23
|
+
mv "$TMP_FILE" "$FILE"
|
|
24
|
+
else
|
|
25
|
+
echo "Shebang already present in $FILE"
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
chmod +x "$FILE"
|
package/src/cli.tsx
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { Command, program } from 'commander'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { promises as fs, read } from 'fs'
|
|
4
|
+
import { findGlotstackConfig } from './util/findConfig'
|
|
5
|
+
import { cwd } from 'process'
|
|
6
|
+
import { merge } from './util/object'
|
|
7
|
+
import { loadYaml } from './util/yaml'
|
|
8
|
+
import eslint from 'eslint'
|
|
9
|
+
import * as readline from "node:readline/promises"
|
|
10
|
+
import { stdin as input, stdout as output } from "node:process"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const fetchGlotstack = async (apiOrigin: string, apiKey: string, body: object) => {
|
|
14
|
+
const url = `${apiOrigin}/api/translations`
|
|
15
|
+
console.info(`Fetching translations from: ${url}`)
|
|
16
|
+
|
|
17
|
+
const headers = {
|
|
18
|
+
'Content-Type': 'application/json',
|
|
19
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const res = await fetch(url, { method: 'POST', body: JSON.stringify(body), headers })
|
|
24
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`)
|
|
25
|
+
return res.json()
|
|
26
|
+
// fs.writeFile(`${outputDir}/source.json`, JSON.stringify(json, null, 2))
|
|
27
|
+
} catch (err) {
|
|
28
|
+
console.error('Fetch failed:', err)
|
|
29
|
+
process.exit(1)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async function run(args: string[]) {
|
|
35
|
+
program
|
|
36
|
+
.command('extract-translations')
|
|
37
|
+
.option('--source-path [path]', 'to source files root directory', '.')
|
|
38
|
+
.option('--api-origin [url]', 'glotstack api origin', process.env.GLOTSTACK_HOST ?? 'https://glotstack.ai')
|
|
39
|
+
.option('--yes', 'skip confirm checks', false)
|
|
40
|
+
.action(async (options: Record<string, any>) => {
|
|
41
|
+
if (!options.apiOrigin) {
|
|
42
|
+
throw new Error('apiOrigin must be specified')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const linter = new eslint.ESLint({ overrideConfigFile: path.join(__dirname, '..', 'eslint-raw-string.mjs') })
|
|
46
|
+
const results = await linter.lintFiles(["./**/*"])
|
|
47
|
+
const filesWithIssues = results
|
|
48
|
+
.filter((r) => r.errorCount + r.warningCount > 0)
|
|
49
|
+
.map((r) => r.filePath)
|
|
50
|
+
|
|
51
|
+
const rl = readline.createInterface({ input, output })
|
|
52
|
+
|
|
53
|
+
const askToSend = async (): Promise<boolean> => {
|
|
54
|
+
if (options.yes) {
|
|
55
|
+
return true
|
|
56
|
+
}
|
|
57
|
+
const response = await rl.question(`Your source are going to be sent to our LLM -- they should not contain any secrets. Proceed? (yes/no):`)
|
|
58
|
+
if (response === 'yes') {
|
|
59
|
+
return true
|
|
60
|
+
} else if (response !== 'no') {
|
|
61
|
+
console.error('Please respond with yes or no.')
|
|
62
|
+
return askToSend()
|
|
63
|
+
} else {
|
|
64
|
+
return false
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const send = await askToSend()
|
|
69
|
+
if (send) {
|
|
70
|
+
console.info('Sending files to LLM')
|
|
71
|
+
rl.close()
|
|
72
|
+
} else {
|
|
73
|
+
rl.close()
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
program
|
|
78
|
+
.command('get-translations')
|
|
79
|
+
.option('--source-path [path]', 'path to en-US.json (or your canonical source json)')
|
|
80
|
+
.option('--api-origin [url]', 'glotstack api origin', process.env.GLOTSTACK_HOST ?? 'https://glotstack.ai')
|
|
81
|
+
.option('--output-dir [path]', 'path to output directory')
|
|
82
|
+
.option('--api-key [key]', 'api key for glotstack.ai')
|
|
83
|
+
.option('--project-id [id]', '(optional) specific project to use')
|
|
84
|
+
.argument('[output-locales...]', 'locales to get translations for')
|
|
85
|
+
.action(async (outputLocales: string[], options: Record<string, any>, command: Command) => {
|
|
86
|
+
const configPath = findGlotstackConfig(cwd())
|
|
87
|
+
let config = {}
|
|
88
|
+
|
|
89
|
+
if (configPath != null) {
|
|
90
|
+
console.info('Loading config file at ', configPath)
|
|
91
|
+
try {
|
|
92
|
+
const text = await fs.readFile(configPath, 'utf-8')
|
|
93
|
+
config = JSON.parse(text)
|
|
94
|
+
console.info('Loaded config file', config)
|
|
95
|
+
} catch (err) {
|
|
96
|
+
//pass
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const resolved = merge(config, options, { outputLocales })
|
|
101
|
+
const { apiOrigin, sourcePath, outputDir, projectId } = resolved
|
|
102
|
+
|
|
103
|
+
if ((resolved.outputLocales as string[]).includes('en-US')) {
|
|
104
|
+
console.warn('en-US detected in outputLocales, removing')
|
|
105
|
+
resolved.outputLocales = resolved.outputLocales.filter((x: string) => x !== 'en-US')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!sourcePath) {
|
|
109
|
+
throw new Error('sourcePath must be specified')
|
|
110
|
+
}
|
|
111
|
+
if (!apiOrigin) {
|
|
112
|
+
throw new Error('apiOrigin must be specified')
|
|
113
|
+
}
|
|
114
|
+
if (!outputDir) {
|
|
115
|
+
throw new Error('outputDir must be specified')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const absPath = path.resolve(sourcePath)
|
|
119
|
+
const fileContent = await fs.readFile(absPath, 'utf-8')
|
|
120
|
+
|
|
121
|
+
let json = null
|
|
122
|
+
try {
|
|
123
|
+
json = loadYaml(fileContent)
|
|
124
|
+
} catch (err) {
|
|
125
|
+
try {
|
|
126
|
+
json = JSON.parse(fileContent)
|
|
127
|
+
} catch (err) {
|
|
128
|
+
console.error('Unable to parse source file ', absPath, err)
|
|
129
|
+
throw err
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const body = {
|
|
134
|
+
locales: resolved.outputLocales,
|
|
135
|
+
translations: json,
|
|
136
|
+
...{ ... (projectId != null ? { projectId } : {}) },
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const data = await fetchGlotstack(apiOrigin, resolved.apiKey, body)
|
|
140
|
+
console.info('Received translations:', data)
|
|
141
|
+
Object.entries(data.data).map(([key, val]) => {
|
|
142
|
+
const p = `${outputDir}/${key}.json`
|
|
143
|
+
console.info(`Writing file ${p}`)
|
|
144
|
+
fs.writeFile(`${outputDir}/${key}.json`, JSON.stringify(val, null, 2))
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
await program.parseAsync(args)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
run(process.argv)
|
package/src/index.tsx
CHANGED
|
@@ -23,125 +23,117 @@ export interface Translations {
|
|
|
23
23
|
|
|
24
24
|
export interface ContextType {
|
|
25
25
|
translations: Translations
|
|
26
|
-
requireTranslations: (localeRegion: string) => Promise<unknown>
|
|
27
|
-
setLocale: (locale: LocaleRegion) => Promise<unknown>
|
|
28
26
|
locale: string | null
|
|
27
|
+
loadTranslations: (locale: LocaleRegion) => Promise<Translations>
|
|
28
|
+
setLocale: (locale: LocaleRegion) => void
|
|
29
|
+
importMethod: (locale: LocaleRegion) => Promise<Translations>
|
|
29
30
|
t: (key: string, options?: { locale?: LocaleRegion }) => string
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
export const GlotstackContext = React.createContext<ContextType>({
|
|
33
34
|
translations: {},
|
|
34
|
-
|
|
35
|
-
setLocale: (_locale: LocaleRegion) =>
|
|
35
|
+
loadTranslations: () => { throw new Error('no import method set') },
|
|
36
|
+
setLocale: (_locale: LocaleRegion) => { throw new Error('import method not set') },
|
|
36
37
|
locale: null,
|
|
37
|
-
|
|
38
|
+
importMethod: (_locale: LocaleRegion) => { throw new Error('import method not set') },
|
|
39
|
+
t: () => { throw new Error('import method not set') },
|
|
38
40
|
})
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
children: React.ReactNode
|
|
42
|
+
interface GlotstackProviderProps {
|
|
43
|
+
children: React.ReactNode
|
|
42
44
|
initialTranslations?: Translations
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
initialLocale?: LocaleRegion
|
|
46
|
+
onTranslationLoaded?: (locale: LocaleRegion, translations: Translations) => void
|
|
47
|
+
onLocaleChange?: (locale: LocaleRegion) => void
|
|
48
|
+
importMethod: (locale: LocaleRegion) => Promise<Translations>
|
|
49
|
+
}
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const requireTranslations = React.useMemo(() => {
|
|
54
|
-
return (locale: string) => {
|
|
55
|
-
const current = translations?.[locale]
|
|
56
|
-
if (current != null) {
|
|
57
|
-
return Promise.resolve()
|
|
58
|
-
}
|
|
59
|
-
if (loadingRef.current?.[locale] == null) {
|
|
60
|
-
loadingRef.current[locale] = import(`domains/application/translations/${locale}.json`)
|
|
61
|
-
}
|
|
62
|
-
loadingRef.current[locale].then(async (Module) => {
|
|
63
|
-
if (Module?.default != null) {
|
|
64
|
-
const data = await (await fetch(Module.default)).json()
|
|
65
|
-
setTranslations(merge({}, translations, { [locale]: data }))
|
|
66
|
-
}
|
|
67
|
-
})
|
|
68
|
-
return loadingRef.current[locale]
|
|
69
|
-
}
|
|
70
|
-
}, [locale, translations])
|
|
51
|
+
export const access = (key: string, locale: LocaleRegion, translations: Translations) => {
|
|
52
|
+
const access = [...key.split('.')] as [LocaleRegion, ...string[]]
|
|
53
|
+
const localeTranslations = translations?.[locale]
|
|
71
54
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return
|
|
76
|
-
}
|
|
77
|
-
requireTranslations(options?.locale)?.then(() => {
|
|
78
|
-
if (options?.locale == null) {
|
|
79
|
-
return
|
|
80
|
-
}
|
|
81
|
-
setLocale(options?.locale)
|
|
82
|
-
})
|
|
83
|
-
}, [locale])
|
|
84
|
-
|
|
85
|
-
if (translations === null) {
|
|
86
|
-
return key
|
|
87
|
-
}
|
|
55
|
+
if (localeTranslations == null) {
|
|
56
|
+
return key
|
|
57
|
+
}
|
|
88
58
|
|
|
89
|
-
|
|
90
|
-
|
|
59
|
+
const value = access.reduce((acc: Translations[string], key) => {
|
|
60
|
+
// @ts-expect-error expected
|
|
61
|
+
return acc?.[key]
|
|
62
|
+
}, localeTranslations)
|
|
91
63
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
64
|
+
return (value?.value ?? key) as string
|
|
65
|
+
}
|
|
95
66
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
67
|
+
export const GlotstackProvider = ({ children, initialLocale, initialTranslations, onLocaleChange, onTranslationLoaded, importMethod }: GlotstackProviderProps) => {
|
|
68
|
+
if (initialLocale == null) {
|
|
69
|
+
throw new Error('initialLocale must be set')
|
|
70
|
+
}
|
|
71
|
+
const [locale, setLocale] = React.useState<LocaleRegion>(initialLocale)
|
|
72
|
+
const translationsRef = React.useRef(initialTranslations)
|
|
73
|
+
const loadingRef = React.useRef<Record<string, Promise<Translations>>>({})
|
|
74
|
+
|
|
75
|
+
const loadTranslations = React.useCallback(async (locale: string) => {
|
|
76
|
+
// TODO: if translations are loaded only reload if some condition is
|
|
77
|
+
try {
|
|
78
|
+
if (loadingRef.current?.[locale] != null) {
|
|
79
|
+
return (await loadingRef.current?.[locale])
|
|
80
|
+
}
|
|
81
|
+
loadingRef.current[locale] = importMethod(locale)
|
|
82
|
+
const result = await loadingRef.current[locale]
|
|
100
83
|
|
|
101
|
-
|
|
102
|
-
|
|
84
|
+
if (result == null) {
|
|
85
|
+
throw new Error(`Failed to load translation ${locale} ${JSON.stringify(result)}`)
|
|
86
|
+
}
|
|
87
|
+
if (translationsRef.current) {
|
|
88
|
+
translationsRef.current[locale] = result
|
|
89
|
+
}
|
|
90
|
+
onTranslationLoaded?.(locale, result)
|
|
91
|
+
return result
|
|
92
|
+
} catch (err) {
|
|
93
|
+
console.error('Unable to import translations', err)
|
|
94
|
+
throw err
|
|
95
|
+
}
|
|
96
|
+
}, [importMethod, onTranslationLoaded])
|
|
103
97
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
React.useEffect(() => {
|
|
99
|
+
const run = async () => {
|
|
100
|
+
onLocaleChange?.(locale)
|
|
101
|
+
const result = await loadTranslations(locale)
|
|
102
|
+
}
|
|
103
|
+
React.startTransition(() => {
|
|
104
|
+
run()
|
|
108
105
|
})
|
|
109
|
-
}, [
|
|
110
|
-
|
|
111
|
-
const valueRef = React.useRef<ContextType>({
|
|
112
|
-
requireTranslations,
|
|
113
|
-
locale,
|
|
114
|
-
translations: initialTranslations ?? {},
|
|
115
|
-
setLocale,
|
|
116
|
-
t
|
|
117
|
-
})
|
|
106
|
+
}, [locale])
|
|
118
107
|
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
React.useEffect(() => {
|
|
122
|
-
valueRef.current = {
|
|
123
|
-
translations,
|
|
124
|
-
requireTranslations,
|
|
108
|
+
const context = React.useMemo(() => {
|
|
109
|
+
return {
|
|
125
110
|
setLocale,
|
|
111
|
+
translations: translationsRef.current ?? {},
|
|
126
112
|
locale,
|
|
127
|
-
|
|
128
|
-
|
|
113
|
+
importMethod,
|
|
114
|
+
loadTranslations,
|
|
115
|
+
t: (key: string, opts?: { locale?: LocaleRegion }) => {
|
|
116
|
+
React.useEffect(() => {
|
|
117
|
+
if (opts?.locale == null) {
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
loadTranslations(opts?.locale)
|
|
121
|
+
}, [locale, opts?.locale])
|
|
122
|
+
return access(key, opts?.locale ?? locale, translationsRef.current ?? {})
|
|
123
|
+
}
|
|
129
124
|
|
|
130
|
-
if (!isEqual(valueRef.current, current)) {
|
|
131
|
-
onUpdated?.(valueRef.current)
|
|
132
125
|
}
|
|
126
|
+
}, [locale, importMethod])
|
|
133
127
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
React.useEffect(() => {
|
|
137
|
-
requireTranslations(locale)
|
|
138
|
-
}, [requireTranslations, locale])
|
|
139
|
-
|
|
140
|
-
return <GlotstackContext.Provider value={valueRef.current}>
|
|
128
|
+
return <GlotstackContext.Provider value={context}>
|
|
141
129
|
{children}
|
|
142
130
|
</GlotstackContext.Provider>
|
|
143
131
|
}
|
|
144
132
|
|
|
133
|
+
export const useGlotstack = () => {
|
|
134
|
+
return React.useContext(GlotstackContext)
|
|
135
|
+
}
|
|
136
|
+
|
|
145
137
|
|
|
146
138
|
export const useTranslations = (_options?: Record<never, never>) => {
|
|
147
139
|
const context = React.useContext(GlotstackContext)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Recursively looks for `.glotstack.json` from the current directory up to the root.
|
|
6
|
+
* @param startDir The directory to start the search from. Defaults to process.cwd().
|
|
7
|
+
* @returns The absolute path to the file if found, or null if not found.
|
|
8
|
+
*/
|
|
9
|
+
export function findGlotstackConfig(startDir: string = process.cwd()): string | null {
|
|
10
|
+
let currentDir = path.resolve(startDir);
|
|
11
|
+
|
|
12
|
+
while (true) {
|
|
13
|
+
const candidate = path.join(currentDir, '.glotstack.json');
|
|
14
|
+
if (fs.existsSync(candidate)) {
|
|
15
|
+
return candidate;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const parentDir = path.dirname(currentDir);
|
|
19
|
+
if (parentDir === currentDir) {
|
|
20
|
+
break; // Reached root
|
|
21
|
+
}
|
|
22
|
+
currentDir = parentDir;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return null;
|
|
26
|
+
}
|
package/src/util/object.ts
CHANGED
|
@@ -25,7 +25,7 @@ type MergeTarget = Record<string|number|symbol, any>
|
|
|
25
25
|
|
|
26
26
|
export function merge(target: MergeTarget, ...sources: MergeTarget[]): MergeTarget {
|
|
27
27
|
return sources.reduce((previous, current) => {
|
|
28
|
-
return
|
|
28
|
+
return _merge(previous, current);
|
|
29
29
|
}, target)
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -49,4 +49,3 @@ export function isEqual(a: any, b: any): boolean {
|
|
|
49
49
|
|
|
50
50
|
return false;
|
|
51
51
|
}
|
|
52
|
-
|
package/src/util/yaml.ts
ADDED
package/tsconfig.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"exclude": ["node_modules", "dist"],
|
|
3
|
-
"include": ["./"],
|
|
3
|
+
"include": ["./src"],
|
|
4
4
|
"files": ["./types/global.d.ts"],
|
|
5
5
|
"compilerOptions": {
|
|
6
6
|
"outDir": "dist",
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
"baseUrl": ".",
|
|
14
14
|
"paths": {
|
|
15
15
|
},
|
|
16
|
-
"rootDirs": ["./src/*"],
|
|
17
16
|
"esModuleInterop": true,
|
|
18
17
|
"skipLibCheck": true,
|
|
19
18
|
"forceConsistentCasingInFileNames": true,
|