@kubb/core 2.0.0-alpha.3 → 2.0.0-alpha.5

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.
Files changed (53) hide show
  1. package/dist/index.cjs +70 -75
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +198 -215
  4. package/dist/index.d.ts +198 -215
  5. package/dist/index.js +70 -73
  6. package/dist/index.js.map +1 -1
  7. package/dist/utils.cjs +19 -8
  8. package/dist/utils.cjs.map +1 -1
  9. package/dist/utils.d.cts +6 -2
  10. package/dist/utils.d.ts +6 -2
  11. package/dist/utils.js +19 -8
  12. package/dist/utils.js.map +1 -1
  13. package/package.json +7 -5
  14. package/src/BarrelManager.ts +123 -0
  15. package/src/FileManager.ts +483 -0
  16. package/src/Generator.ts +34 -0
  17. package/src/PackageManager.ts +163 -0
  18. package/src/PluginManager.ts +644 -0
  19. package/src/PromiseManager.ts +47 -0
  20. package/src/SchemaGenerator.ts +8 -0
  21. package/src/build.ts +207 -0
  22. package/src/config.ts +22 -0
  23. package/src/errors.ts +12 -0
  24. package/src/index.ts +28 -0
  25. package/src/plugin.ts +80 -0
  26. package/src/types.ts +346 -0
  27. package/src/utils/EventEmitter.ts +24 -0
  28. package/src/utils/FunctionParams.ts +85 -0
  29. package/src/utils/Queue.ts +110 -0
  30. package/src/utils/TreeNode.ts +122 -0
  31. package/src/utils/URLPath.ts +128 -0
  32. package/src/utils/cache.ts +35 -0
  33. package/src/utils/clean.ts +5 -0
  34. package/src/utils/executeStrategies.ts +71 -0
  35. package/src/utils/index.ts +19 -0
  36. package/src/utils/logger.ts +76 -0
  37. package/src/utils/promise.ts +13 -0
  38. package/src/utils/randomColour.ts +39 -0
  39. package/src/utils/read.ts +68 -0
  40. package/src/utils/renderTemplate.ts +31 -0
  41. package/src/utils/throttle.ts +30 -0
  42. package/src/utils/timeout.ts +7 -0
  43. package/src/utils/transformers/combineCodes.ts +3 -0
  44. package/src/utils/transformers/createJSDocBlockText.ts +15 -0
  45. package/src/utils/transformers/escape.ts +31 -0
  46. package/src/utils/transformers/indent.ts +3 -0
  47. package/src/utils/transformers/index.ts +22 -0
  48. package/src/utils/transformers/nameSorter.ts +9 -0
  49. package/src/utils/transformers/searchAndReplace.ts +25 -0
  50. package/src/utils/transformers/transformReservedWord.ts +97 -0
  51. package/src/utils/transformers/trim.ts +3 -0
  52. package/src/utils/uniqueName.ts +20 -0
  53. package/src/utils/write.ts +63 -0
@@ -0,0 +1,39 @@
1
+ import pc from 'picocolors'
2
+ import seedrandom from 'seedrandom'
3
+
4
+ import type { Formatter } from 'picocolors/types.ts'
5
+
6
+ const defaultColours = ['black', 'blue', 'darkBlue', 'cyan', 'gray', 'green', 'darkGreen', 'magenta', 'red', 'darkRed', 'yellow', 'darkYellow'] as const
7
+
8
+ export function randomColour(text?: string, colours = defaultColours): string {
9
+ if (!text) {
10
+ return 'white'
11
+ }
12
+
13
+ const random = seedrandom(text)
14
+ const colour = colours.at(Math.floor(random() * colours.length)) || 'white'
15
+
16
+ return colour
17
+ }
18
+
19
+ export function randomPicoColour(text?: string, colors = defaultColours): string {
20
+ const colours = pc.createColors(true)
21
+
22
+ if (!text) {
23
+ return colours.white(text)
24
+ }
25
+
26
+ const colour = randomColour(text, colors)
27
+ const isDark = colour.includes('dark')
28
+ const key = colour.replace('dark', '').toLowerCase() as keyof typeof colours
29
+ const formatter: Formatter = colours[key] as Formatter
30
+
31
+ if (isDark) {
32
+ return pc.bold(formatter(text))
33
+ }
34
+
35
+ if (typeof formatter !== 'function') {
36
+ throw new Error('Formatter for picoColor is not of type function/Formatter')
37
+ }
38
+ return formatter(text)
39
+ }
@@ -0,0 +1,68 @@
1
+ import { basename, extname, relative } from 'node:path'
2
+
3
+ import fs from 'fs-extra'
4
+ import { switcher } from 'js-runtime'
5
+
6
+ function slash(path: string, platform: 'windows' | 'mac' | 'linux' = 'linux') {
7
+ const isWindowsPath = /^\\\\\?\\/.test(path)
8
+
9
+ if (['linux', 'mac'].includes(platform) && !isWindowsPath) {
10
+ // linux and mac
11
+ return path.replaceAll(/\\/g, '/').replace('../', '').trimEnd()
12
+ }
13
+
14
+ // windows
15
+ return path.replaceAll(/\\/g, '/').replace('../', '').trimEnd()
16
+ }
17
+
18
+ export function getRelativePath(rootDir?: string | null, filePath?: string | null, platform: 'windows' | 'mac' | 'linux' = 'linux'): string {
19
+ if (!rootDir || !filePath) {
20
+ throw new Error(`Root and file should be filled in when retrieving the relativePath, ${rootDir || ''} ${filePath || ''}`)
21
+ }
22
+
23
+ const relativePath = relative(rootDir, filePath)
24
+
25
+ // On Windows, paths are separated with a "\"
26
+ // However, web browsers use "/" no matter the platform
27
+ const slashedPath = slash(relativePath, platform)
28
+
29
+ if (slashedPath.startsWith('../')) {
30
+ return slashedPath.replace(basename(slashedPath), basename(slashedPath, extname(filePath)))
31
+ }
32
+
33
+ return `./${slashedPath.replace(basename(slashedPath), basename(slashedPath, extname(filePath)))}`
34
+ }
35
+
36
+ const reader = switcher(
37
+ {
38
+ node: async (path: string) => {
39
+ return fs.readFile(path, { encoding: 'utf8' })
40
+ },
41
+ bun: async (path: string) => {
42
+ const file = Bun.file(path)
43
+
44
+ return file.text()
45
+ },
46
+ },
47
+ 'node',
48
+ )
49
+
50
+ const syncReader = switcher(
51
+ {
52
+ node: (path: string) => {
53
+ return fs.readFileSync(path, { encoding: 'utf8' })
54
+ },
55
+ bun: () => {
56
+ throw new Error('Bun cannot read sync')
57
+ },
58
+ },
59
+ 'node',
60
+ )
61
+
62
+ export async function read(path: string): Promise<string> {
63
+ return reader(path)
64
+ }
65
+
66
+ export function readSync(path: string): string {
67
+ return syncReader(path)
68
+ }
@@ -0,0 +1,31 @@
1
+ export function renderTemplate<TData extends Record<string, unknown> = Record<string, unknown>>(template: string, data: TData | undefined = undefined): string {
2
+ if (!data || !Object.keys(data).length) {
3
+ return template.replace(/{{(.*?)}}/g, '')
4
+ }
5
+
6
+ const matches = template.match(/{{(.*?)}}/g)
7
+
8
+ return (
9
+ matches?.reduce((prev, curr) => {
10
+ const index = curr.split(/{{|}}/).filter(Boolean)[0]?.trim()
11
+ if (index === undefined) {
12
+ return prev
13
+ }
14
+ const value = data[index]
15
+
16
+ if (value === undefined) {
17
+ return prev
18
+ }
19
+
20
+ return prev
21
+ .replace(curr, () => {
22
+ if (typeof value === 'boolean') {
23
+ return `${value.toString()}` || 'false'
24
+ }
25
+
26
+ return (value as string) || ''
27
+ })
28
+ .trim()
29
+ }, template) || ''
30
+ )
31
+ }
@@ -0,0 +1,30 @@
1
+ export const throttle = <R, A extends any[]>(fn: (...args: A) => R, delay: number): [(...args: A) => R | undefined, () => void] => {
2
+ let wait = false
3
+ let timeout: NodeJS.Timeout
4
+ let cancelled = false
5
+
6
+ return [
7
+ (...args: A) => {
8
+ if (cancelled) {
9
+ return undefined
10
+ }
11
+ if (wait) {
12
+ return undefined
13
+ }
14
+
15
+ const val = fn(...args)
16
+
17
+ wait = true
18
+
19
+ timeout = setTimeout(() => {
20
+ wait = false
21
+ }, delay)
22
+
23
+ return val
24
+ },
25
+ () => {
26
+ cancelled = true
27
+ clearTimeout(timeout)
28
+ },
29
+ ]
30
+ }
@@ -0,0 +1,7 @@
1
+ export async function timeout(ms: number): Promise<unknown> {
2
+ return new Promise((resolve) => {
3
+ setTimeout(() => {
4
+ resolve(true)
5
+ }, ms)
6
+ })
7
+ }
@@ -0,0 +1,3 @@
1
+ export function combineCodes(codes: string[]): string {
2
+ return codes.join('\n')
3
+ }
@@ -0,0 +1,15 @@
1
+ export function createJSDocBlockText({ comments, newLine }: { comments: Array<string>; newLine?: boolean }): string {
2
+ const filteredComments = comments.filter(Boolean)
3
+
4
+ if (!filteredComments.length) {
5
+ return ''
6
+ }
7
+
8
+ const source = `/**\n * ${filteredComments.join('\n * ')}\n */`
9
+
10
+ if (newLine) {
11
+ return `${source}\n`
12
+ }
13
+
14
+ return source
15
+ }
@@ -0,0 +1,31 @@
1
+ export function escape(text?: string): string {
2
+ return text ? text.replaceAll('`', '\\`') : ''
3
+ }
4
+
5
+ /**
6
+ * Escape all characters not included in SingleStringCharacters and DoubleStringCharacters on
7
+ * @link http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
8
+ * @link https://github.com/joliss/js-string-escape/blob/master/index.js
9
+ */
10
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
11
+ export function jsStringEscape(input: any): string {
12
+ return `${input}`.replace(/["'\\\n\r\u2028\u2029]/g, (character) => {
13
+ switch (character) {
14
+ case '"':
15
+ case "'":
16
+ case '\\':
17
+ return '\\' + character
18
+ // Four possible LineTerminator characters need to be escaped:
19
+ case '\n':
20
+ return '\\n'
21
+ case '\r':
22
+ return '\\r'
23
+ case '\u2028':
24
+ return '\\u2028'
25
+ case '\u2029':
26
+ return '\\u2029'
27
+ default:
28
+ return ''
29
+ }
30
+ })
31
+ }
@@ -0,0 +1,3 @@
1
+ export function createIndent(size: number): string {
2
+ return Array.from({ length: size + 1 }).join(' ')
3
+ }
@@ -0,0 +1,22 @@
1
+ import { combineCodes } from './combineCodes.ts'
2
+ import { createJSDocBlockText } from './createJSDocBlockText.ts'
3
+ import { escape, jsStringEscape } from './escape.ts'
4
+ import { createIndent } from './indent.ts'
5
+ import { nameSorter } from './nameSorter.ts'
6
+ import { searchAndReplace } from './searchAndReplace.ts'
7
+ import { transformReservedWord } from './transformReservedWord.ts'
8
+ import { trim } from './trim.ts'
9
+
10
+ export const transformers = {
11
+ combineCodes,
12
+ escape,
13
+ jsStringEscape,
14
+ createIndent,
15
+ transformReservedWord,
16
+ nameSorter,
17
+ searchAndReplace,
18
+ trim,
19
+ JSDoc: {
20
+ createJSDocBlockText,
21
+ },
22
+ } as const
@@ -0,0 +1,9 @@
1
+ export function nameSorter<T extends { name: string }>(a: T, b: T): 0 | 1 | -1 {
2
+ if (a.name < b.name) {
3
+ return -1
4
+ }
5
+ if (a.name > b.name) {
6
+ return 1
7
+ }
8
+ return 0
9
+ }
@@ -0,0 +1,25 @@
1
+ type Options = {
2
+ text: string
3
+ replaceBy: string
4
+ prefix?: string
5
+ key: string
6
+ searchValues?: (prefix: string, key: string) => Array<RegExp | string>
7
+ }
8
+
9
+ export function searchAndReplace(options: Options): string {
10
+ const { text, replaceBy, prefix = '', key } = options
11
+
12
+ const searchValues = options.searchValues?.(prefix, key) || [
13
+ `${prefix}["${key}"]`,
14
+ `${prefix}['${key}']`,
15
+ `${prefix}[\`${key}\`]`,
16
+ `${prefix}"${key}"`,
17
+ `${prefix}'${key}'`,
18
+ `${prefix}\`${key}\``,
19
+ new RegExp(`${prefix}${key}`, 'g'),
20
+ ]
21
+
22
+ return searchValues.reduce((prev, searchValue) => {
23
+ return prev.toString().replaceAll(searchValue, replaceBy)
24
+ }, text) as string
25
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
3
+ */
4
+ const reservedWords = [
5
+ 'abstract',
6
+ 'arguments',
7
+ 'boolean',
8
+ 'break',
9
+ 'byte',
10
+ 'case',
11
+ 'catch',
12
+ 'char',
13
+ 'class',
14
+ 'const',
15
+ 'continue',
16
+ 'debugger',
17
+ 'default',
18
+ 'delete',
19
+ 'do',
20
+ 'double',
21
+ 'else',
22
+ 'enum',
23
+ 'eval',
24
+ 'export',
25
+ 'extends',
26
+ 'false',
27
+ 'final',
28
+ 'finally',
29
+ 'float',
30
+ 'for',
31
+ 'function',
32
+ 'goto',
33
+ 'if',
34
+ 'implements',
35
+ 'import',
36
+ 'in',
37
+ 'instanceof',
38
+ 'int',
39
+ 'interface',
40
+ 'let',
41
+ 'long',
42
+ 'native',
43
+ 'new',
44
+ 'null',
45
+ 'package',
46
+ 'private',
47
+ 'protected',
48
+ 'public',
49
+ 'return',
50
+ 'short',
51
+ 'static',
52
+ 'super',
53
+ 'switch',
54
+ 'synchronized',
55
+ 'this',
56
+ 'throw',
57
+ 'throws',
58
+ 'transient',
59
+ 'true',
60
+ 'try',
61
+ 'typeof',
62
+ 'var',
63
+ 'void',
64
+ 'volatile',
65
+ 'while',
66
+ 'with',
67
+ 'yield',
68
+
69
+ 'Array',
70
+ 'Date',
71
+ 'eval',
72
+ 'function',
73
+ 'hasOwnProperty',
74
+ 'Infinity',
75
+ 'isFinite',
76
+ 'isNaN',
77
+ 'isPrototypeOf',
78
+ 'length',
79
+ 'Math',
80
+ 'name',
81
+ 'NaN',
82
+ 'Number',
83
+ 'Object',
84
+ 'prototype',
85
+ 'String',
86
+ 'toString',
87
+ 'undefined',
88
+ 'valueOf',
89
+ ]
90
+
91
+ export function transformReservedWord(word: string): string {
92
+ if ((word && reservedWords.includes(word)) || word?.match(/^\d/)) {
93
+ return `_${word}`
94
+ }
95
+
96
+ return word
97
+ }
@@ -0,0 +1,3 @@
1
+ export function trim(text: string): string {
2
+ return text.replaceAll(/\n/g, '').trim()
3
+ }
@@ -0,0 +1,20 @@
1
+ export function getUniqueName(originalName: string, data: Record<string, number>): string {
2
+ let used = data[originalName] || 0
3
+ if (used) {
4
+ data[originalName] = ++used
5
+ originalName += used
6
+ }
7
+ data[originalName] = 1
8
+ return originalName
9
+ }
10
+
11
+ export function setUniqueName(originalName: string, data: Record<string, number>): string {
12
+ let used = data[originalName] || 0
13
+ if (used) {
14
+ data[originalName] = ++used
15
+
16
+ return originalName
17
+ }
18
+ data[originalName] = 1
19
+ return originalName
20
+ }
@@ -0,0 +1,63 @@
1
+ import { dirname, resolve } from 'node:path'
2
+
3
+ import fs from 'fs-extra'
4
+ import { switcher } from 'js-runtime'
5
+
6
+ async function saveCreateDirectory(path: string): Promise<void> {
7
+ // resolve the full path and get just the directory, ignoring the file and extension
8
+ const passedPath = dirname(resolve(path))
9
+ // make the directory, recursively. Theoretically, if every directory in the path exists, this won't do anything.
10
+ await fs.mkdir(passedPath, { recursive: true })
11
+ }
12
+
13
+ const writer = switcher(
14
+ {
15
+ node: async (path: string, data: string) => {
16
+ try {
17
+ await fs.stat(resolve(path))
18
+ const oldContent = await fs.readFile(resolve(path), { encoding: 'utf-8' })
19
+ if (oldContent?.toString() === data?.toString()) {
20
+ return
21
+ }
22
+ } catch (_err) {
23
+ /* empty */
24
+ }
25
+
26
+ await saveCreateDirectory(path)
27
+ await fs.writeFile(resolve(path), data, { encoding: 'utf-8' })
28
+
29
+ const savedData = await fs.readFile(resolve(path), { encoding: 'utf-8' })
30
+
31
+ if (savedData?.toString() !== data?.toString()) {
32
+ throw new Error(`Sanity check failed for ${path}\n\nData[${data.length}]:\n${data}\n\nSaved[${savedData.length}]:\n${savedData}\n`)
33
+ }
34
+
35
+ return savedData
36
+ },
37
+ bun: async (path: string, data: string) => {
38
+ try {
39
+ await saveCreateDirectory(path)
40
+ await Bun.write(resolve(path), data)
41
+
42
+ const file = Bun.file(resolve(path))
43
+ const savedData = await file.text()
44
+
45
+ if (savedData?.toString() !== data?.toString()) {
46
+ throw new Error(`Sanity check failed for ${path}\n\nData[${data.length}]:\n${data}\n\nSaved[${savedData.length}]:\n${savedData}\n`)
47
+ }
48
+
49
+ return savedData
50
+ } catch (e) {
51
+ console.log(e, resolve(path))
52
+ }
53
+ },
54
+ },
55
+ 'node',
56
+ )
57
+
58
+ export async function write(data: string, path: string): Promise<string | undefined> {
59
+ if (data.trim() === '') {
60
+ return undefined
61
+ }
62
+ return writer(path, data.trim())
63
+ }