@kubb/core
Advanced tools
Comparing version 0.24.0 to 0.27.1
import EventEmitter from 'events'; | ||
type GenerateTreeNodeOptions = { | ||
extensions?: RegExp; | ||
exclude?: RegExp[]; | ||
}; | ||
declare class TreeNode<T = unknown> { | ||
data: T; | ||
parent?: TreeNode<T>; | ||
children: Array<TreeNode<T>>; | ||
constructor(data: T, parent?: TreeNode<T>); | ||
addChild(data: T): any; | ||
find(data: T): any; | ||
leaves(): this[]; | ||
root(): any; | ||
forEach(callback: (treeNode: TreeNode<T>) => void): this; | ||
static generate(path: string, options?: GenerateTreeNodeOptions): TreeNode<{ | ||
name: string; | ||
path: string; | ||
type: "file" | "directory"; | ||
}>; | ||
} | ||
/** | ||
@@ -30,7 +51,7 @@ * @deprecated Use userFile instead | ||
*/ | ||
name: string; | ||
fileName: string; | ||
/** | ||
* FileName will be the end result so no input.path will not be added | ||
* Path will be full qualified path to a specified file | ||
*/ | ||
fileName: string; | ||
path: string; | ||
source: string; | ||
@@ -48,2 +69,4 @@ }; | ||
private cache; | ||
private readonly root; | ||
private readonly output; | ||
emitter: EventEmitter; | ||
@@ -62,6 +85,15 @@ events: { | ||
}; | ||
constructor(); | ||
constructor(options?: { | ||
root?: string; | ||
output?: string; | ||
}); | ||
private getCache; | ||
private getCountOfStatus; | ||
get files(): Map<string, CacheStore>; | ||
get files(): File[]; | ||
get tree(): TreeNode<{ | ||
name: string; | ||
path: string; | ||
type: "file" | "directory"; | ||
}>; | ||
generateRootFiles(options: GenerateTreeNodeOptions): Promise<void>[]; | ||
add(file: File): Promise<File>; | ||
@@ -149,3 +181,3 @@ setStatus(id: UUID, status: Status): void; | ||
buildStart: (this: PluginContext, kubbConfig: KubbConfig) => MaybePromise<void>; | ||
resolveId: (this: PluginContext, importee: string, importer?: string, options?: Record<string, any>) => string | null | undefined; | ||
resolveId: (this: PluginContext, importee: string, importer?: string, options?: Record<string, any>) => Path; | ||
load: (this: PluginContext, id: string) => MaybePromise<TransformResult | null>; | ||
@@ -171,6 +203,7 @@ transform: (this: PluginContext, code: string, id: string) => MaybePromise<TransformResult>; | ||
fileManager: FileManager; | ||
emitFile: (emitedFile: EmittedFile | File) => Promise<File>; | ||
resolveId: (params: ResolveIdParams) => MaybePromise<string | null | undefined>; | ||
addFile: (emitedFile: EmittedFile | File, options?: { | ||
root?: true; | ||
}) => Promise<File>; | ||
resolveId: (params: ResolveIdParams) => MaybePromise<Path>; | ||
load: (id: string) => MaybePromise<TransformResult | void>; | ||
addToRoot: (file: File) => void; | ||
}; | ||
@@ -181,2 +214,7 @@ type ValidationResult = true | { | ||
type TransformResult = string | null; | ||
/** | ||
* @description Computing the name of a file or directory together with its position in relation to other directories traced back in a line to the root | ||
*/ | ||
type Path = string | null | undefined; | ||
type FileName = string | null | undefined; | ||
type LogType = 'error' | 'warn' | 'info'; | ||
@@ -233,3 +271,3 @@ type LogLevel = LogType | 'silent'; | ||
declare const getRelativePath: (from?: string | null, to?: string | null) => string; | ||
declare const getRelativePath: (root?: string | null, file?: string | null) => string; | ||
@@ -253,3 +291,3 @@ /** | ||
}); | ||
resolveId: (params: ResolveIdParams) => Promise<string | null | undefined>; | ||
resolveId: (params: ResolveIdParams) => Promise<Path>; | ||
load: (id: string) => Promise<TransformResult>; | ||
@@ -279,2 +317,2 @@ hookForPlugin<H extends PluginLifecycleHooks>(pluginName: string, hookName: H, parameters: Parameters<PluginLifecycle[H]>, skipped?: ReadonlySet<KubbPlugin> | null): Promise<ReturnType<PluginLifecycle[H]> | null>; | ||
export { Argument0, CLIOptions, Cache, CacheStore, CorePluginOptions, EmittedFile, File, FileManager, KubbConfig, KubbPlugin, KubbUserConfig, LogLevel, LogType, Logger, MaybePromise, PluginContext, PluginFactoryOptions, PluginLifecycle, PluginLifecycleHooks, PluginManager, ResolveIdParams, Status, Strategy, TransformResult, UUID, ValidationResult, build, createPlugin, createPluginCache, build as default, defineConfig, format, getRelativePath, hooks, isPromise, write }; | ||
export { Argument0, CLIOptions, Cache, CacheStore, CorePluginOptions, EmittedFile, File, FileManager, FileName, GenerateTreeNodeOptions, KubbConfig, KubbPlugin, KubbUserConfig, LogLevel, LogType, Logger, MaybePromise, Path, PluginContext, PluginFactoryOptions, PluginLifecycle, PluginLifecycleHooks, PluginManager, ResolveIdParams, Status, Strategy, TransformResult, TreeNode, UUID, ValidationResult, build, createPlugin, createPluginCache, build as default, defineConfig, format, getRelativePath, hooks, isPromise, write }; |
@@ -5,3 +5,3 @@ 'use strict'; | ||
var path2 = require('path'); | ||
var path3 = require('path'); | ||
var fse = require('fs-extra'); | ||
@@ -11,2 +11,3 @@ var prettier = require('prettier'); | ||
var crypto = require('crypto'); | ||
var dirTree = require('directory-tree'); | ||
@@ -33,7 +34,7 @@ // src/build.ts | ||
// src/utils/write.ts | ||
var write = async (data, path4, options = { format: false }) => { | ||
var write = async (data, path5, options = { format: false }) => { | ||
const formattedData = options.format ? format(data) : data; | ||
try { | ||
await fse.stat(path4); | ||
const oldContent = await fse.readFile(path4, { encoding: "utf-8" }); | ||
await fse.stat(path5); | ||
const oldContent = await fse.readFile(path5, { encoding: "utf-8" }); | ||
if (oldContent?.toString() === formattedData) { | ||
@@ -43,5 +44,5 @@ return; | ||
} catch (_err) { | ||
return fse.outputFile(path4, formattedData); | ||
return fse.outputFile(path5, formattedData); | ||
} | ||
return fse.outputFile(path4, formattedData); | ||
return fse.outputFile(path5, formattedData); | ||
}; | ||
@@ -74,7 +75,7 @@ | ||
} | ||
var getRelativePath = (from, to) => { | ||
if (!from || !to) { | ||
throw new Error("From and to should be filled in when retrieving the relativePath"); | ||
var getRelativePath = (root, file) => { | ||
if (!root || !file) { | ||
throw new Error("Root and file should be filled in when retrieving the relativePath"); | ||
} | ||
const newPath = path2.relative(from, to).replace("../", "").replace(".ts", "").trimEnd(); | ||
const newPath = path3.relative(root, file).replace("../", "").replace(".ts", "").trimEnd(); | ||
return `./${newPath}`; | ||
@@ -104,3 +105,2 @@ }; | ||
const { fileManager, resolveId, load } = options; | ||
const indexFiles = []; | ||
const api = { | ||
@@ -111,17 +111,15 @@ get config() { | ||
fileManager, | ||
async emitFile(emittedFile) { | ||
if (isEmittedFile(emittedFile)) { | ||
const resolvedId = await resolveId({ importee: emittedFile.id, importer: emittedFile.importer, options: emittedFile.options }); | ||
const fileName = resolvedId || emittedFile.importer || emittedFile.id; | ||
async addFile(file, options2) { | ||
if (isEmittedFile(file)) { | ||
const resolvedId = await resolveId({ importee: file.id, importer: file.importer, options: file.options }); | ||
const filePath = resolvedId || file.importer || file.id; | ||
return fileManager.add({ | ||
fileName, | ||
name: emittedFile.name || emittedFile.id, | ||
source: emittedFile.source || "" | ||
path: filePath, | ||
fileName: file.name || file.id, | ||
source: file.source || "" | ||
}); | ||
} | ||
return fileManager.add(emittedFile); | ||
if (options2?.root) ; | ||
return fileManager.add(file); | ||
}, | ||
addToRoot: (file) => { | ||
indexFiles.push(file); | ||
}, | ||
resolveId, | ||
@@ -135,11 +133,3 @@ load, | ||
async buildEnd() { | ||
if (!indexFiles.length) { | ||
return; | ||
} | ||
let index = ``; | ||
indexFiles.forEach((item) => { | ||
index += `export * from "${getRelativePath(path2.resolve(this.config.root, this.config.output.path), item.fileName)}"; | ||
`; | ||
}); | ||
await write(index, path2.resolve(this.config.root, this.config.output.path, "index.ts"), { format: true }); | ||
await this.fileManager.generateRootFiles({ exclude: [/schemas/, /json/] }); | ||
}, | ||
@@ -150,3 +140,3 @@ resolveId(importee, importer) { | ||
} | ||
return path2.resolve(importer, importee); | ||
return path3.resolve(importer, importee); | ||
} | ||
@@ -158,3 +148,3 @@ }; | ||
var keys = { | ||
getEmitFileKey: () => `emit-file`, | ||
getFileKey: () => `file`, | ||
getStatusChangeKey: () => `status-change`, | ||
@@ -168,3 +158,3 @@ getStatusChangeByIdKey: (id) => `${id}-status-change`, | ||
emitFile: (id, file) => { | ||
emitter.emit(keys.getEmitFileKey(), id, file); | ||
emitter.emit(keys.getFileKey(), id, file); | ||
}, | ||
@@ -184,4 +174,4 @@ emitStatusChange: (file) => { | ||
onAdd: (callback) => { | ||
emitter.on(keys.getEmitFileKey(), callback); | ||
return () => emitter.removeListener(keys.getEmitFileKey(), callback); | ||
emitter.on(keys.getFileKey(), callback); | ||
return () => emitter.removeListener(keys.getFileKey(), callback); | ||
}, | ||
@@ -206,2 +196,78 @@ onStatusChange: (callback) => { | ||
}; | ||
var TreeNode = class { | ||
data; | ||
parent; | ||
children; | ||
constructor(data, parent) { | ||
this.data = data; | ||
this.parent = parent; | ||
return this; | ||
} | ||
addChild(data) { | ||
const child = new TreeNode(data, this); | ||
if (!this.children) { | ||
this.children = []; | ||
} | ||
this.children.push(child); | ||
return child; | ||
} | ||
find(data) { | ||
if (data === this.data) { | ||
return this; | ||
} | ||
if (this.children) { | ||
for (let i = 0, { length } = this.children, target = null; i < length; i++) { | ||
target = this.children[i].find(data); | ||
if (target) { | ||
return target; | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
leaves() { | ||
if (!this.children || this.children.length === 0) { | ||
return [this]; | ||
} | ||
const leaves = []; | ||
if (this.children) { | ||
for (let i = 0, { length } = this.children; i < length; i++) { | ||
leaves.push.apply(leaves, this.children[i].leaves()); | ||
} | ||
} | ||
return leaves; | ||
} | ||
root() { | ||
if (!this.parent) { | ||
return this; | ||
} | ||
return this.parent.root(); | ||
} | ||
forEach(callback) { | ||
if (typeof callback !== "function") { | ||
throw new TypeError("forEach() callback must be a function"); | ||
} | ||
callback(this); | ||
if (this.children) { | ||
for (let i = 0, { length } = this.children; i < length; i++) { | ||
this.children[i].forEach(callback); | ||
} | ||
} | ||
return this; | ||
} | ||
static generate(path5, options = {}) { | ||
const filteredTree = dirTree(path5, { extensions: options?.extensions, exclude: options.exclude }); | ||
const treeNode = new TreeNode({ name: filteredTree.name, path: filteredTree.path, type: filteredTree.type }); | ||
const recurse = (node, item) => { | ||
const subNode = node.addChild({ name: item.name, path: item.path, type: item.type }); | ||
if (item.children?.length) { | ||
item.children?.forEach((child) => { | ||
recurse(subNode, child); | ||
}); | ||
} | ||
}; | ||
filteredTree.children?.forEach((child) => recurse(treeNode, child)); | ||
return treeNode; | ||
} | ||
}; | ||
@@ -211,5 +277,9 @@ // src/managers/fileManager/FileManager.ts | ||
cache = /* @__PURE__ */ new Map(); | ||
root; | ||
output; | ||
emitter = new EventEmitter(); | ||
events = getFileManagerEvents(this.emitter); | ||
constructor() { | ||
constructor(options = {}) { | ||
this.root = path3.resolve(options.root || "."); | ||
this.output = options?.output || process.cwd(); | ||
this.events.onStatusChange(() => { | ||
@@ -234,4 +304,50 @@ if (this.getCountOfStatus("removed") === this.cache.size) { | ||
get files() { | ||
return this.cache; | ||
const files = []; | ||
this.cache.forEach((item) => { | ||
files.push(item.file); | ||
}); | ||
return files; | ||
} | ||
get tree() { | ||
return TreeNode.generate(this.output, { extensions: /\.ts/ }); | ||
} | ||
generateRootFiles(options) { | ||
const tree = TreeNode.generate(this.output, { extensions: /\.ts/, ...options }); | ||
const fileReducer = (files2, item) => { | ||
if (!item.children) { | ||
return []; | ||
} | ||
if (item.children?.length > 1) { | ||
const source = item.children.reduce((source2, file) => { | ||
if (!file) { | ||
return source2; | ||
} | ||
const importPath = file.data.type === "directory" ? `./${file.data.name}` : `./${file.data.name.replace(/\.[^.]*$/, "")}`; | ||
return `${source2}export * from "${importPath}"; | ||
`; | ||
}, ""); | ||
files2.push({ | ||
path: path3.resolve(this.root, item.data.path, "index.ts"), | ||
fileName: "index.ts", | ||
source | ||
}); | ||
} else { | ||
item.children?.forEach((child) => { | ||
const importPath = child.data.type === "directory" ? `./${child.data.name}` : `./${child.data.name.replace(/\.[^.]*$/, "")}`; | ||
files2.push({ | ||
path: path3.resolve(this.root, item.data.path, "index.ts"), | ||
fileName: "index.ts", | ||
source: `export * from "${importPath}"; | ||
` | ||
}); | ||
}); | ||
} | ||
item.children.forEach((childItem) => { | ||
fileReducer(files2, childItem); | ||
}); | ||
return files2; | ||
}; | ||
const files = fileReducer([], tree); | ||
return files.map((file) => write(file.source, file.path)); | ||
} | ||
add(file) { | ||
@@ -285,3 +401,3 @@ const cacheItem = { id: crypto.randomUUID(), file, status: "new" }; | ||
plugins; | ||
fileManager = new FileManager(); | ||
fileManager; | ||
logger; | ||
@@ -293,2 +409,3 @@ config; | ||
this.config = config; | ||
this.fileManager = new FileManager({ root: this.config.root, output: this.config.output.path }); | ||
this.core = definePlugin({ | ||
@@ -436,3 +553,3 @@ config, | ||
const { plugins, fileManager } = pluginManager; | ||
const input = fse.readFileSync(path2.resolve(config.root, config.input.path), "utf-8"); | ||
fse.readFileSync(path3.resolve(config.root, config.input.path), "utf-8"); | ||
const validations = await pluginManager.hookParallel("validate", [plugins]); | ||
@@ -448,9 +565,12 @@ const validationsWithMessage = validations.filter(Boolean); | ||
} | ||
fileManager.events.onSuccess(async () => { | ||
await pluginManager.hookParallel("buildEnd"); | ||
setTimeout(() => { | ||
done(); | ||
}, 1e3); | ||
}); | ||
fileManager.events.onAdd(async (id, file) => { | ||
let fileName = file.fileName; | ||
let { name: name2, source: code } = file; | ||
if (!fileName && name2) { | ||
fileName = path2.resolve(config.root, config.output.path, name2); | ||
} | ||
const loadedResult = await pluginManager.hookFirst("load", [fileName]); | ||
const { path: filePath } = file; | ||
let { source: code } = file; | ||
const loadedResult = await pluginManager.hookFirst("load", [filePath]); | ||
if (loadedResult) { | ||
@@ -460,4 +580,4 @@ code = loadedResult; | ||
if (code) { | ||
const transformedCode = await pluginManager.hookReduceArg0("transform", [code, fileName], transformReducer); | ||
await pluginManager.hookParallel("writeFile", [transformedCode, fileName]); | ||
const transformedCode = await pluginManager.hookReduceArg0("transform", [code, filePath], transformReducer); | ||
await pluginManager.hookParallel("writeFile", [transformedCode, filePath]); | ||
fileManager.setStatus(id, "success"); | ||
@@ -468,13 +588,2 @@ fileManager.remove(id); | ||
await pluginManager.hookParallel("buildStart", [config]); | ||
fileManager.add({ | ||
fileName: path2.resolve(config.root, config.input.path), | ||
name: "root", | ||
source: input | ||
}); | ||
fileManager.events.onSuccess(async () => { | ||
await pluginManager.hookParallel("buildEnd"); | ||
setTimeout(() => { | ||
done(); | ||
}, 1e3); | ||
}); | ||
} | ||
@@ -499,2 +608,3 @@ function build(options) { | ||
exports.PluginManager = PluginManager; | ||
exports.TreeNode = TreeNode; | ||
exports.build = build; | ||
@@ -501,0 +611,0 @@ exports.createPlugin = createPlugin; |
{ | ||
"name": "@kubb/core", | ||
"version": "0.24.0", | ||
"version": "0.27.1", | ||
"description": "Generator core", | ||
@@ -36,2 +36,3 @@ "repository": { | ||
"change-case": "^4.1.2", | ||
"directory-tree": "^3.5.1", | ||
"events": "^3.3.0", | ||
@@ -38,0 +39,0 @@ "fs-extra": "^11.1.0", |
@@ -1,1 +0,42 @@ | ||
# Demo | ||
<div align="center"> | ||
<!-- <img src="assets/logo.png" alt="logo" width="200" height="auto" /> --> | ||
<h1>@kubb/core</h1> | ||
<p> | ||
With Kubb you can create and use plugins that will generate code based on a specific input. | ||
</p> | ||
<!-- Badges --> | ||
<p> | ||
<a href="https://www.npmjs.com/package/@kubb/core"> | ||
<img alt="Npm verssion" src="https://img.shields.io/npm/v/@kubb/core?style=for-the-badge"/> | ||
</a> | ||
<a href="https://www.npmjs.com/package/@kubb/core"> | ||
<img alt="Npm downloads" src="https://img.shields.io/bundlephobia/min/@kubb/core?style=for-the-badge"/> | ||
</a> | ||
<a href="https://www.npmjs.com/package/@kubb/core"> | ||
<img alt="Npm downloads" src="https://img.shields.io/npm/dm/@kubb/core?style=for-the-badge"/> | ||
</a> | ||
</p> | ||
<h4> | ||
<a href="https://codesandbox.io/s/github/stijnvanhulle/kubb/tree/main/examples/simple">View Demo</a> | ||
<span> · </span> | ||
<a href="https://kubb.dev/" target="_blank">Documentation</a> | ||
<span> · </span> | ||
<a href="https://github.com/stijnvanhulle/kubb/issues/">Report Bug</a> | ||
<span> · </span> | ||
<a href="https://github.com/stijnvanhulle/kubb/issues/">Request Feature</a> | ||
</h4> | ||
</div> | ||
<br /> | ||
<!-- About the Project | ||
## :star2: About the Project | ||
<div align="center"> | ||
<img src="assets/screenshot.jpg" alt="screenshot" /> | ||
</div> | ||
--> |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
130342
1519
43
6
+ Addeddirectory-tree@^3.5.1
+ Addedansi-styles@3.2.1(transitive)
+ Addedarray-back@3.1.04.0.2(transitive)
+ Addedchalk@2.4.2(transitive)
+ Addedcolor-convert@1.9.3(transitive)
+ Addedcolor-name@1.1.3(transitive)
+ Addedcommand-line-args@5.2.1(transitive)
+ Addedcommand-line-usage@6.1.3(transitive)
+ Addeddeep-extend@0.6.0(transitive)
+ Addeddirectory-tree@3.5.2(transitive)
+ Addedescape-string-regexp@1.0.5(transitive)
+ Addedfind-replace@3.0.0(transitive)
+ Addedhas-flag@3.0.0(transitive)
+ Addedlodash.camelcase@4.3.0(transitive)
+ Addedreduce-flatten@2.0.0(transitive)
+ Addedsupports-color@5.5.0(transitive)
+ Addedtable-layout@1.0.2(transitive)
+ Addedtypical@4.0.05.2.0(transitive)
+ Addedwordwrapjs@4.0.1(transitive)