BREAKING(structure): modernize internal structure and support unpacking
This commit is contained in:
180
ts/mod_config/classes.tsconfig.ts
Normal file
180
ts/mod_config/classes.tsconfig.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import type { CompilerOptions } from 'typescript';
|
||||
import typescript from 'typescript';
|
||||
|
||||
/**
|
||||
* Default compiler options for TypeScript compilation
|
||||
*/
|
||||
export const compilerOptionsDefault: CompilerOptions = {
|
||||
declaration: true,
|
||||
inlineSourceMap: true,
|
||||
noEmitOnError: true,
|
||||
outDir: 'dist_ts/',
|
||||
module: typescript.ModuleKind.NodeNext,
|
||||
target: typescript.ScriptTarget.ESNext,
|
||||
moduleResolution: typescript.ModuleResolutionKind.NodeNext,
|
||||
lib: ['lib.dom.d.ts', 'lib.esnext.d.ts'],
|
||||
noImplicitAny: false,
|
||||
esModuleInterop: true,
|
||||
verbatimModuleSyntax: true,
|
||||
baseUrl: './',
|
||||
};
|
||||
|
||||
/**
|
||||
* TsConfig handles loading and merging TypeScript compiler configurations.
|
||||
* It supports reading tsconfig.json and merging with defaults and custom options.
|
||||
*/
|
||||
export class TsConfig {
|
||||
private cwd: string;
|
||||
private cachedTsConfig: CompilerOptions | null = null;
|
||||
|
||||
constructor(cwd: string = process.cwd()) {
|
||||
this.cwd = cwd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current working directory
|
||||
*/
|
||||
public getCwd(): string {
|
||||
return this.cwd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and parse tsconfig.json from the current directory
|
||||
*/
|
||||
public load(): CompilerOptions {
|
||||
if (this.cachedTsConfig !== null) {
|
||||
return this.cachedTsConfig;
|
||||
}
|
||||
|
||||
let tsconfig: any;
|
||||
|
||||
try {
|
||||
const tsconfigPath = path.join(this.cwd, 'tsconfig.json');
|
||||
const tsconfigContent = fs.readFileSync(tsconfigPath, 'utf8');
|
||||
tsconfig = JSON.parse(tsconfigContent);
|
||||
} catch {
|
||||
this.cachedTsConfig = {};
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!tsconfig || !tsconfig.compilerOptions) {
|
||||
this.cachedTsConfig = {};
|
||||
return {};
|
||||
}
|
||||
|
||||
const returnObject: CompilerOptions = { ...tsconfig.compilerOptions };
|
||||
|
||||
// Convert target string to enum
|
||||
if (tsconfig.compilerOptions.target && typeof tsconfig.compilerOptions.target === 'string') {
|
||||
const targetKey = tsconfig.compilerOptions.target.toUpperCase();
|
||||
if (targetKey in typescript.ScriptTarget) {
|
||||
returnObject.target = typescript.ScriptTarget[targetKey as keyof typeof typescript.ScriptTarget];
|
||||
}
|
||||
}
|
||||
|
||||
// Convert module string to enum
|
||||
if (tsconfig.compilerOptions.module && typeof tsconfig.compilerOptions.module === 'string') {
|
||||
const moduleKey = tsconfig.compilerOptions.module.toUpperCase();
|
||||
if (moduleKey in typescript.ModuleKind) {
|
||||
returnObject.module = typescript.ModuleKind[moduleKey as keyof typeof typescript.ModuleKind];
|
||||
} else if (moduleKey === 'NODENEXT') {
|
||||
returnObject.module = typescript.ModuleKind.NodeNext;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert moduleResolution string to enum
|
||||
if (tsconfig.compilerOptions.moduleResolution && typeof tsconfig.compilerOptions.moduleResolution === 'string') {
|
||||
const moduleResolutionKey = tsconfig.compilerOptions.moduleResolution.toUpperCase();
|
||||
if (moduleResolutionKey in typescript.ModuleResolutionKind) {
|
||||
returnObject.moduleResolution = typescript.ModuleResolutionKind[
|
||||
moduleResolutionKey as keyof typeof typescript.ModuleResolutionKind
|
||||
];
|
||||
} else if (moduleResolutionKey === 'NODENEXT') {
|
||||
returnObject.moduleResolution = typescript.ModuleResolutionKind.NodeNext;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply path transformations (ts_ → dist_ts_)
|
||||
if (tsconfig.compilerOptions.paths) {
|
||||
returnObject.paths = { ...tsconfig.compilerOptions.paths };
|
||||
for (const pathKey of Object.keys(returnObject.paths)) {
|
||||
if (Array.isArray(returnObject.paths[pathKey]) && returnObject.paths[pathKey].length > 0) {
|
||||
returnObject.paths[pathKey][0] = returnObject.paths[pathKey][0].replace('./ts_', './dist_ts_');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.cachedTsConfig = returnObject;
|
||||
return returnObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default compiler options
|
||||
*/
|
||||
public getDefaultOptions(): CompilerOptions {
|
||||
return { ...compilerOptionsDefault };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get critical/protected default options that shouldn't be overridden by tsconfig.json
|
||||
*/
|
||||
public getProtectedDefaults(): CompilerOptions {
|
||||
return {
|
||||
outDir: 'dist_ts/',
|
||||
noEmitOnError: true,
|
||||
declaration: true,
|
||||
inlineSourceMap: true,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Process command line arguments and return applicable compiler options
|
||||
*/
|
||||
public getCommandLineOptions(argvArg?: any): CompilerOptions {
|
||||
if (!argvArg) return {};
|
||||
|
||||
const options: CompilerOptions = {};
|
||||
|
||||
if (argvArg.skiplibcheck) {
|
||||
options.skipLibCheck = true;
|
||||
}
|
||||
|
||||
if (argvArg.disallowimplicitany) {
|
||||
options.noImplicitAny = true;
|
||||
}
|
||||
|
||||
if (argvArg.commonjs) {
|
||||
options.module = typescript.ModuleKind.CommonJS;
|
||||
options.moduleResolution = typescript.ModuleResolutionKind.NodeJs;
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge compiler options with proper priority order:
|
||||
* 1. Default options
|
||||
* 2. tsconfig.json options
|
||||
* 3. Protected defaults (cannot be overridden by tsconfig)
|
||||
* 4. Custom options (programmatic)
|
||||
* 5. CLI options (highest priority)
|
||||
*/
|
||||
public merge(customOptions: CompilerOptions = {}, argvArg?: any): CompilerOptions {
|
||||
return {
|
||||
...this.getDefaultOptions(),
|
||||
...this.load(),
|
||||
...this.getProtectedDefaults(),
|
||||
...customOptions,
|
||||
...this.getCommandLineOptions(argvArg),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cached tsconfig (useful for reloading)
|
||||
*/
|
||||
public clearCache(): void {
|
||||
this.cachedTsConfig = null;
|
||||
}
|
||||
}
|
||||
116
ts/mod_config/classes.tspublishconfig.ts
Normal file
116
ts/mod_config/classes.tspublishconfig.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
/**
|
||||
* Interface for tspublish.json configuration
|
||||
*/
|
||||
export interface ITsPublishJson {
|
||||
order?: number;
|
||||
unpack?: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* TsPublishConfig handles loading and parsing tspublish.json files.
|
||||
* These configuration files control module-specific settings like
|
||||
* compilation order and output unpacking behavior.
|
||||
*/
|
||||
export class TsPublishConfig {
|
||||
private folderPath: string;
|
||||
private cachedConfig: ITsPublishJson | null | undefined = undefined;
|
||||
|
||||
constructor(folderPath: string) {
|
||||
this.folderPath = folderPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the folder path this config is for
|
||||
*/
|
||||
public getFolderPath(): string {
|
||||
return this.folderPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and parse tspublish.json from the folder
|
||||
* Returns null if file doesn't exist or is invalid
|
||||
*/
|
||||
public async load(): Promise<ITsPublishJson | null> {
|
||||
if (this.cachedConfig !== undefined) {
|
||||
return this.cachedConfig;
|
||||
}
|
||||
|
||||
try {
|
||||
const configPath = path.join(this.folderPath, 'tspublish.json');
|
||||
const content = await fs.promises.readFile(configPath, 'utf8');
|
||||
this.cachedConfig = JSON.parse(content);
|
||||
return this.cachedConfig;
|
||||
} catch {
|
||||
this.cachedConfig = null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously load and parse tspublish.json from the folder
|
||||
* Returns null if file doesn't exist or is invalid
|
||||
*/
|
||||
public loadSync(): ITsPublishJson | null {
|
||||
if (this.cachedConfig !== undefined) {
|
||||
return this.cachedConfig;
|
||||
}
|
||||
|
||||
try {
|
||||
const configPath = path.join(this.folderPath, 'tspublish.json');
|
||||
const content = fs.readFileSync(configPath, 'utf8');
|
||||
this.cachedConfig = JSON.parse(content);
|
||||
return this.cachedConfig;
|
||||
} catch {
|
||||
this.cachedConfig = null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if output should be unpacked (flattened)
|
||||
* Default is true if not specified
|
||||
*/
|
||||
public get shouldUnpack(): boolean {
|
||||
const config = this.loadSync();
|
||||
if (!config || config.unpack === undefined) {
|
||||
return true; // Default to true
|
||||
}
|
||||
return config.unpack === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the compilation order for tsfolders command
|
||||
* Returns Infinity if not specified (sorted last)
|
||||
*/
|
||||
public get order(): number {
|
||||
const config = this.loadSync();
|
||||
if (!config || config.order === undefined) {
|
||||
return Infinity;
|
||||
}
|
||||
return config.order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if tspublish.json exists in the folder
|
||||
*/
|
||||
public async exists(): Promise<boolean> {
|
||||
try {
|
||||
const configPath = path.join(this.folderPath, 'tspublish.json');
|
||||
await fs.promises.access(configPath, fs.constants.F_OK);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cached config (useful for reloading)
|
||||
*/
|
||||
public clearCache(): void {
|
||||
this.cachedConfig = undefined;
|
||||
}
|
||||
}
|
||||
2
ts/mod_config/index.ts
Normal file
2
ts/mod_config/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './classes.tsconfig.js';
|
||||
export * from './classes.tspublishconfig.js';
|
||||
Reference in New Issue
Block a user