@izumisy-tailor/omakase-modules 0.1.0 → 0.3.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/README.md +47 -0
- package/docs/tutorials/creating-modules.md +296 -0
- package/docs/tutorials/using-modules.md +224 -0
- package/package.json +17 -28
- package/src/builder/helpers.ts +91 -0
- package/src/builder/index.ts +9 -0
- package/src/builder/register.ts +50 -0
- package/src/config/index.ts +1 -0
- package/src/config/module-loader.ts +93 -0
- package/src/config/sdk/index.ts +1 -0
- package/src/config/sdk/paths.ts +49 -0
- package/src/config/sdk/wrapper/base.ts +141 -0
- package/src/config/sdk/wrapper/generator.ts +52 -0
- package/src/config/sdk/wrapper/strategies.ts +102 -0
- package/dist/builder/index.d.mts +0 -3
- package/dist/builder/index.mjs +0 -35
- package/dist/config/index.d.mts +0 -2
- package/dist/config/index.mjs +0 -62
- package/dist/config/sdk/index.d.mts +0 -10
- package/dist/config/sdk/index.mjs +0 -14
- package/dist/helpers-CNjRbrYN.d.mts +0 -29
- package/dist/index-BoAL29Di.d.mts +0 -38
- package/dist/module-loader-B4sA1i0A.d.mts +0 -38
- package/dist/stub-loader/index.d.mts +0 -2
- package/dist/stub-loader/index.mjs +0 -31
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { TailorDBType } from "@tailor-platform/sdk";
|
|
2
|
+
|
|
3
|
+
type ModuleBuilderProps<C extends Record<string, unknown>> = {
|
|
4
|
+
config: C;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type ConfiguredModule<
|
|
8
|
+
C extends Record<string, unknown> = Record<string, unknown>
|
|
9
|
+
> = {
|
|
10
|
+
packageName: string;
|
|
11
|
+
moduleProps: ModuleBuilderProps<C>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type DefinedModule<
|
|
15
|
+
C extends Record<string, unknown>,
|
|
16
|
+
Tables extends Record<string, unknown> = Record<string, unknown>
|
|
17
|
+
> = {
|
|
18
|
+
packageName: string;
|
|
19
|
+
configure: (props: ModuleBuilderProps<C>) => ConfiguredModule<C>;
|
|
20
|
+
/**
|
|
21
|
+
* @internal Type-only hook so that table shapes flow through helper utilities.
|
|
22
|
+
*/
|
|
23
|
+
readonly __tablesBrand?: Tables;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const defineModule = <
|
|
27
|
+
C extends Record<string, unknown>,
|
|
28
|
+
Tables extends Record<string, unknown> = Record<string, unknown>
|
|
29
|
+
>(baseProps: {
|
|
30
|
+
/**
|
|
31
|
+
* The package name of the module.
|
|
32
|
+
*
|
|
33
|
+
* This is required to be the same as the name field in package.json
|
|
34
|
+
*/
|
|
35
|
+
packageName: string;
|
|
36
|
+
}): DefinedModule<C, Tables> => {
|
|
37
|
+
return {
|
|
38
|
+
packageName: baseProps.packageName,
|
|
39
|
+
configure: (props) => {
|
|
40
|
+
return {
|
|
41
|
+
packageName: baseProps.packageName,
|
|
42
|
+
moduleProps: props,
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type ModuleDependency<T extends DefinedModule<any, any>> =
|
|
49
|
+
T extends DefinedModule<infer C, infer Tables>
|
|
50
|
+
? ConfiguredModule<C> & {
|
|
51
|
+
readonly __tablesBrand?: Tables;
|
|
52
|
+
}
|
|
53
|
+
: never;
|
|
54
|
+
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// Utility Types for Module Development
|
|
57
|
+
// ============================================================================
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Derives a tables type from a tableNames array.
|
|
61
|
+
* Use this to avoid manually defining a separate Tables type.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* export const tableNames = ["product", "category"] as const;
|
|
66
|
+
* type MyTables = TablesFromNames<typeof tableNames>;
|
|
67
|
+
* // Result: { product: TailorDBType; category: TailorDBType }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export type TablesFromNames<T extends readonly string[]> = {
|
|
71
|
+
[K in T[number]]: TailorDBType;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Context passed to table builder functions.
|
|
76
|
+
* Provides access to the module's configuration.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* export const buildProductTable = (
|
|
81
|
+
* { config }: ModuleFactoryContext<ModuleConfig>,
|
|
82
|
+
* deps: { category: TailorDBType }
|
|
83
|
+
* ) => {
|
|
84
|
+
* const prefix = config.dataModel?.product?.docNumberPrefix ?? "PROD";
|
|
85
|
+
* return db.type("Product", { ... });
|
|
86
|
+
* };
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export type ModuleFactoryContext<C extends Record<string, unknown>> = {
|
|
90
|
+
config: C;
|
|
91
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { LoadedModules } from "../config/module-loader";
|
|
2
|
+
import type { DefinedModule } from "./helpers";
|
|
3
|
+
|
|
4
|
+
type ModuleFactoryContext<C extends Record<string, unknown>> = {
|
|
5
|
+
config: C;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Define module exports that depend on configuration from loadModules.
|
|
10
|
+
*
|
|
11
|
+
* This function returns a factory function that takes LoadedModules as input
|
|
12
|
+
* and produces the configured exports. The wrapper files generated by
|
|
13
|
+
* getModulesReference will call this factory with the app's loadModules result.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // In module's tailordb/index.ts
|
|
18
|
+
* export default withModuleConfiguration(moduleDef, (context) => {
|
|
19
|
+
* const inventory = buildInventoryTable(context);
|
|
20
|
+
* return { inventory };
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // In module's executors that need dependency module tables:
|
|
24
|
+
* export default withModuleConfiguration(moduleDef, async (context, loadedModules) => {
|
|
25
|
+
* const { productVariant } = await loadedModules.getTables(commerceModuleTables);
|
|
26
|
+
* return createExecutor({ ... });
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // The wrapper file will call it like:
|
|
30
|
+
* // import factory from "module/backend/tailordb";
|
|
31
|
+
* // import modules from "../../modules";
|
|
32
|
+
* // export default await factory(modules);
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export const withModuleConfiguration = <
|
|
36
|
+
C extends Record<string, unknown>,
|
|
37
|
+
Tables extends Record<string, unknown>,
|
|
38
|
+
Result
|
|
39
|
+
>(
|
|
40
|
+
module: DefinedModule<C, Tables>,
|
|
41
|
+
factory: (
|
|
42
|
+
context: ModuleFactoryContext<C>,
|
|
43
|
+
loadedModules: LoadedModules
|
|
44
|
+
) => Result | Promise<Result>
|
|
45
|
+
): ((loadedModules: LoadedModules) => Promise<Result>) => {
|
|
46
|
+
return async (loadedModules: LoadedModules) => {
|
|
47
|
+
const moduleState = loadedModules.loadConfig<C>(module);
|
|
48
|
+
return await factory(moduleState, loadedModules);
|
|
49
|
+
};
|
|
50
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { loadModules, ModuleLoader, type LoadedModules } from "./module-loader";
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { ConfiguredModule } from "../builder/helpers";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Module loader
|
|
5
|
+
* Builder for registering modules within loadModules
|
|
6
|
+
*/
|
|
7
|
+
export class ModuleLoader {
|
|
8
|
+
private modules: Array<ConfiguredModule<any>> = [];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Add a module to the loader
|
|
12
|
+
* @param module Configured module
|
|
13
|
+
* @returns The added module (can be used as a dependency for other modules)
|
|
14
|
+
*/
|
|
15
|
+
add<C extends Record<string, unknown>>(
|
|
16
|
+
module: ConfiguredModule<C>
|
|
17
|
+
): ConfiguredModule<C> {
|
|
18
|
+
this.modules.push(module);
|
|
19
|
+
return module;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get all registered modules
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
_getModules(): Array<ConfiguredModule<any>> {
|
|
27
|
+
return this.modules;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type LoadedModules = {
|
|
32
|
+
loadedModules: Record<string, ConfiguredModule<any>>;
|
|
33
|
+
loadConfig: <C extends Record<string, unknown>>(module: {
|
|
34
|
+
packageName: string;
|
|
35
|
+
}) => {
|
|
36
|
+
config: C;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Get the tables from a dependency module by calling its factory function.
|
|
40
|
+
* This is used when an executor or resolver needs to reference tables from another module.
|
|
41
|
+
*
|
|
42
|
+
* @param factory The factory function exported by the dependency module's tailordb/index.ts
|
|
43
|
+
* @returns The tables created by the factory
|
|
44
|
+
*/
|
|
45
|
+
getTables: <T>(
|
|
46
|
+
factory: (loadedModules: LoadedModules) => Promise<T>
|
|
47
|
+
) => Promise<T>;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Load modules with configuration
|
|
52
|
+
* @param configurator Function that receives a loader, registers modules, and returns the loader
|
|
53
|
+
* @returns Loaded modules and loadConfig function
|
|
54
|
+
*/
|
|
55
|
+
export const loadModules = (
|
|
56
|
+
configurator: (loader: ModuleLoader) => ModuleLoader
|
|
57
|
+
): LoadedModules => {
|
|
58
|
+
const emptyLoader = new ModuleLoader();
|
|
59
|
+
const modules = configurator(emptyLoader)._getModules();
|
|
60
|
+
|
|
61
|
+
const loadedModules = modules.reduce<Record<string, ConfiguredModule<any>>>(
|
|
62
|
+
(acc, module) => {
|
|
63
|
+
acc[module.packageName] = module;
|
|
64
|
+
return acc;
|
|
65
|
+
},
|
|
66
|
+
{}
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const loadedModulesResult: LoadedModules = {
|
|
70
|
+
loadedModules,
|
|
71
|
+
loadConfig: <C extends Record<string, unknown>>(module: {
|
|
72
|
+
packageName: string;
|
|
73
|
+
}) => {
|
|
74
|
+
const loadedModule = loadedModules[module.packageName];
|
|
75
|
+
if (!loadedModule) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`Module "${module.packageName}" has not been configured. Ensure it is added via loadModules.`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
config: loadedModule.moduleProps.config as C,
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
getTables: async <T>(
|
|
86
|
+
factory: (loadedModules: LoadedModules) => Promise<T>
|
|
87
|
+
): Promise<T> => {
|
|
88
|
+
return factory(loadedModulesResult);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return loadedModulesResult;
|
|
93
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { getModulesReference } from "./paths";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { LoadedModules } from "../module-loader";
|
|
3
|
+
import { generateWrapperFiles, OMAKASE_WRAPPER_DIR } from "./wrapper/generator";
|
|
4
|
+
|
|
5
|
+
export type GetModulesReferenceOptions = {
|
|
6
|
+
/**
|
|
7
|
+
* Base path for the application (defaults to process.cwd())
|
|
8
|
+
*/
|
|
9
|
+
basePath?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Whether to suppress log output (defaults to false)
|
|
12
|
+
*/
|
|
13
|
+
silent?: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const getModulesReference = async (
|
|
17
|
+
loadedModules: LoadedModules,
|
|
18
|
+
options: GetModulesReferenceOptions = {}
|
|
19
|
+
) => {
|
|
20
|
+
const { basePath = process.cwd(), silent = false } = options;
|
|
21
|
+
|
|
22
|
+
// Log loaded modules information
|
|
23
|
+
const modulePackageNames = Object.keys(loadedModules.loadedModules);
|
|
24
|
+
if (!silent) {
|
|
25
|
+
console.log(`[omakase] Loaded ${modulePackageNames.length} module(s):\n`);
|
|
26
|
+
for (const name of modulePackageNames) {
|
|
27
|
+
console.log(` * ${name}`);
|
|
28
|
+
}
|
|
29
|
+
console.log("");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Generate wrapper files and return paths to them
|
|
33
|
+
const wrapperPaths = await generateWrapperFiles(loadedModules, basePath);
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
tailordb:
|
|
37
|
+
wrapperPaths.tailordb.length > 0
|
|
38
|
+
? [path.join(OMAKASE_WRAPPER_DIR, "*", "tailordb", "*.ts")]
|
|
39
|
+
: [],
|
|
40
|
+
resolver:
|
|
41
|
+
wrapperPaths.resolver.length > 0
|
|
42
|
+
? [path.join(OMAKASE_WRAPPER_DIR, "*", "resolvers", "*.ts")]
|
|
43
|
+
: [],
|
|
44
|
+
executor:
|
|
45
|
+
wrapperPaths.executor.length > 0
|
|
46
|
+
? [path.join(OMAKASE_WRAPPER_DIR, "*", "executors", "*.ts")]
|
|
47
|
+
: [],
|
|
48
|
+
};
|
|
49
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import dedent from "dedent";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Path alias for importing modules config.
|
|
7
|
+
* App's tsconfig.json should have: "@omakase-modules/config": ["./modules"]
|
|
8
|
+
*/
|
|
9
|
+
export const MODULES_IMPORT_PATH = "@omakase-modules/config";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Category types for wrapper generation
|
|
13
|
+
*/
|
|
14
|
+
export type Category = "tailordb" | "resolvers" | "executors";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Abstract base class for category-specific wrapper generation
|
|
18
|
+
*/
|
|
19
|
+
export abstract class WrapperStrategy {
|
|
20
|
+
constructor(
|
|
21
|
+
protected readonly packageName: string,
|
|
22
|
+
protected readonly basePath: string
|
|
23
|
+
) {}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The category this strategy handles
|
|
27
|
+
*/
|
|
28
|
+
abstract readonly category: Category;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Filter files to process for this category
|
|
32
|
+
*/
|
|
33
|
+
abstract filterFiles(files: string[]): string[];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get the module export path
|
|
37
|
+
*/
|
|
38
|
+
abstract getExportPath(fileName: string): string;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Generate the export statements using the factory result.
|
|
42
|
+
* Default implementation exports the result as default export.
|
|
43
|
+
*/
|
|
44
|
+
protected generateExports(): Promise<string> {
|
|
45
|
+
return Promise.resolve("export default result;");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Generate wrapper file content using template method
|
|
50
|
+
*
|
|
51
|
+
* The common header, imports, and factory call are included here.
|
|
52
|
+
*/
|
|
53
|
+
async generateContent(moduleExportPath: string): Promise<string> {
|
|
54
|
+
const exports = await this.generateExports();
|
|
55
|
+
|
|
56
|
+
return dedent`
|
|
57
|
+
/**
|
|
58
|
+
* Auto-generated wrapper file by @izumisy-tailor/omakase-modules
|
|
59
|
+
* DO NOT EDIT THIS FILE MANUALLY
|
|
60
|
+
*
|
|
61
|
+
* This file calls the module's factory function with the app's loadModules result,
|
|
62
|
+
* ensuring proper configuration injection and avoiding tree-shaking issues.
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
// Import the loadModules result from the app's modules.ts
|
|
66
|
+
import modules from "${MODULES_IMPORT_PATH}";
|
|
67
|
+
|
|
68
|
+
// Import the factory function from the module
|
|
69
|
+
import createFactory from "${moduleExportPath}";
|
|
70
|
+
|
|
71
|
+
// Call the factory with loadModules result
|
|
72
|
+
const result = await createFactory(modules);
|
|
73
|
+
|
|
74
|
+
${exports}
|
|
75
|
+
`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Generate wrapper files for this category
|
|
80
|
+
*/
|
|
81
|
+
async generateFiles(wrapperDir: string): Promise<string[]> {
|
|
82
|
+
const sourcePath = path.join(
|
|
83
|
+
this.basePath,
|
|
84
|
+
"node_modules",
|
|
85
|
+
this.packageName,
|
|
86
|
+
"src",
|
|
87
|
+
this.category
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
// Check if the source directory exists
|
|
91
|
+
if (!(await this.exists(sourcePath))) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const allFiles = await fs.readdir(sourcePath);
|
|
96
|
+
const files = this.filterFiles(allFiles);
|
|
97
|
+
const wrapperPaths: string[] = [];
|
|
98
|
+
|
|
99
|
+
for (const file of files) {
|
|
100
|
+
const wrapperPath = await this.generateFile(wrapperDir, file);
|
|
101
|
+
wrapperPaths.push(wrapperPath);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return wrapperPaths;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Generate a single wrapper file for this category
|
|
109
|
+
*/
|
|
110
|
+
private async generateFile(
|
|
111
|
+
wrapperDir: string,
|
|
112
|
+
fileName: string
|
|
113
|
+
): Promise<string> {
|
|
114
|
+
const categoryWrapperDir = path.join(
|
|
115
|
+
wrapperDir,
|
|
116
|
+
this.packageName,
|
|
117
|
+
this.category
|
|
118
|
+
);
|
|
119
|
+
await fs.mkdir(categoryWrapperDir, { recursive: true });
|
|
120
|
+
|
|
121
|
+
const wrapperFilePath = path.join(categoryWrapperDir, fileName);
|
|
122
|
+
const moduleExportPath = this.getExportPath(fileName);
|
|
123
|
+
const content = await this.generateContent(moduleExportPath);
|
|
124
|
+
|
|
125
|
+
await fs.writeFile(wrapperFilePath, content, "utf-8");
|
|
126
|
+
|
|
127
|
+
return wrapperFilePath;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Check if a path exists
|
|
132
|
+
*/
|
|
133
|
+
private async exists(filePath: string): Promise<boolean> {
|
|
134
|
+
try {
|
|
135
|
+
await fs.access(filePath);
|
|
136
|
+
return true;
|
|
137
|
+
} catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { LoadedModules } from "../../module-loader";
|
|
4
|
+
import { createStrategy } from "./strategies";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Directory where wrapper files are generated
|
|
8
|
+
*/
|
|
9
|
+
export const OMAKASE_WRAPPER_DIR = ".tailor-sdk/.omakase";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Generates wrapper files for each module's tailordb, executor, and resolver files.
|
|
13
|
+
* These wrappers import the factory functions from modules and call them with
|
|
14
|
+
* the loadModules result from the app's modules.ts.
|
|
15
|
+
*/
|
|
16
|
+
export const generateWrapperFiles = async (
|
|
17
|
+
loadedModules: LoadedModules,
|
|
18
|
+
basePath: string = process.cwd()
|
|
19
|
+
) => {
|
|
20
|
+
const wrapperDir = path.join(basePath, OMAKASE_WRAPPER_DIR);
|
|
21
|
+
const modulePackageNames = Object.keys(loadedModules.loadedModules);
|
|
22
|
+
|
|
23
|
+
// Clean up existing wrapper directory
|
|
24
|
+
try {
|
|
25
|
+
await fs.access(wrapperDir);
|
|
26
|
+
await fs.rm(wrapperDir, { recursive: true });
|
|
27
|
+
} catch {
|
|
28
|
+
// Directory doesn't exist, no need to clean up
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const result = {
|
|
32
|
+
tailordb: [] as string[],
|
|
33
|
+
resolver: [] as string[],
|
|
34
|
+
executor: [] as string[],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
for (const packageName of modulePackageNames) {
|
|
38
|
+
const strategyFor = createStrategy(packageName, basePath);
|
|
39
|
+
|
|
40
|
+
result.tailordb.push(
|
|
41
|
+
...(await strategyFor("tailordb").generateFiles(wrapperDir))
|
|
42
|
+
);
|
|
43
|
+
result.resolver.push(
|
|
44
|
+
...(await strategyFor("resolvers").generateFiles(wrapperDir))
|
|
45
|
+
);
|
|
46
|
+
result.executor.push(
|
|
47
|
+
...(await strategyFor("executors").generateFiles(wrapperDir))
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return result;
|
|
52
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { Category, WrapperStrategy } from "./base";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Strategy for tailordb category
|
|
7
|
+
*/
|
|
8
|
+
class TailorDBStrategy extends WrapperStrategy {
|
|
9
|
+
readonly category = "tailordb" as const;
|
|
10
|
+
filterFiles(files: string[]) {
|
|
11
|
+
return files.filter((file) => file === "index.ts");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getExportPath() {
|
|
15
|
+
return `${this.packageName}/backend/tailordb`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
protected async generateExports() {
|
|
19
|
+
const tableNames = await this.importTableNames();
|
|
20
|
+
return tableNames
|
|
21
|
+
.map((name) => `export const ${name}Table = result.${name};`)
|
|
22
|
+
.join("\n");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Import tableNames from a module's tailordb/index.ts using dynamic import.
|
|
27
|
+
* Uses createRequire to leverage Node.js module resolution from the app's context.
|
|
28
|
+
*/
|
|
29
|
+
private async importTableNames(): Promise<readonly string[]> {
|
|
30
|
+
try {
|
|
31
|
+
const appRequire = createRequire(
|
|
32
|
+
path.join(this.basePath, "package.json")
|
|
33
|
+
);
|
|
34
|
+
const modulePath = appRequire.resolve(
|
|
35
|
+
`${this.packageName}/backend/tailordb`
|
|
36
|
+
);
|
|
37
|
+
const module = await import(`file://${modulePath}`);
|
|
38
|
+
if (!module.tableNames) {
|
|
39
|
+
console.warn(
|
|
40
|
+
`[warn] tableNames not found in ${this.packageName}/backend/tailordb. Expected: export const tableNames = [...] as const;`
|
|
41
|
+
);
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
return module.tableNames;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.warn(
|
|
47
|
+
`[warn] Failed to import ${this.packageName}/backend/tailordb:`,
|
|
48
|
+
error
|
|
49
|
+
);
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Strategy for resolvers category
|
|
57
|
+
*/
|
|
58
|
+
class ResolversStrategy extends WrapperStrategy {
|
|
59
|
+
readonly category = "resolvers" as const;
|
|
60
|
+
|
|
61
|
+
filterFiles(files: string[]) {
|
|
62
|
+
return files.filter((file) => file.endsWith(".ts") && file !== "index.ts");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
getExportPath(fileName: string) {
|
|
66
|
+
const baseName = fileName.replace(/\.ts$/, "");
|
|
67
|
+
return `${this.packageName}/backend/resolvers/${baseName}`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Strategy for executors category
|
|
73
|
+
*/
|
|
74
|
+
class ExecutorsStrategy extends WrapperStrategy {
|
|
75
|
+
readonly category = "executors" as const;
|
|
76
|
+
|
|
77
|
+
filterFiles(files: string[]) {
|
|
78
|
+
return files.filter((file) => file.endsWith(".ts") && file !== "index.ts");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getExportPath(fileName: string) {
|
|
82
|
+
const baseName = fileName.replace(/\.ts$/, "");
|
|
83
|
+
return `${this.packageName}/backend/executors/${baseName}`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Factory to create strategy instance for each category.
|
|
89
|
+
* Returns a function that takes a category and returns a strategy.
|
|
90
|
+
*/
|
|
91
|
+
export const createStrategy =
|
|
92
|
+
(packageName: string, basePath: string) =>
|
|
93
|
+
(category: Category): WrapperStrategy => {
|
|
94
|
+
switch (category) {
|
|
95
|
+
case "tailordb":
|
|
96
|
+
return new TailorDBStrategy(packageName, basePath);
|
|
97
|
+
case "resolvers":
|
|
98
|
+
return new ResolversStrategy(packageName, basePath);
|
|
99
|
+
case "executors":
|
|
100
|
+
return new ExecutorsStrategy(packageName, basePath);
|
|
101
|
+
}
|
|
102
|
+
};
|
package/dist/builder/index.d.mts
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import { i as defineModule, n as DefinedModule, r as ModuleDependency, t as ConfiguredModule } from "../helpers-CNjRbrYN.mjs";
|
|
2
|
-
import { t as withModuleConfiguration } from "../index-BoAL29Di.mjs";
|
|
3
|
-
export { ConfiguredModule, DefinedModule, ModuleDependency, defineModule, withModuleConfiguration };
|
package/dist/builder/index.mjs
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
//#region src/builder/helpers.ts
|
|
2
|
-
const defineModule = (baseProps) => {
|
|
3
|
-
return {
|
|
4
|
-
packageName: baseProps.packageName,
|
|
5
|
-
configure: (props) => {
|
|
6
|
-
return {
|
|
7
|
-
packageName: baseProps.packageName,
|
|
8
|
-
moduleProps: props
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
//#endregion
|
|
15
|
-
//#region src/builder/register.ts
|
|
16
|
-
/**
|
|
17
|
-
* Load a module's configuration, define something.
|
|
18
|
-
*
|
|
19
|
-
* THis is a low-level utility composed by a specific builder function.
|
|
20
|
-
*/
|
|
21
|
-
const withModuleConfiguration = async (module, factory) => {
|
|
22
|
-
/**
|
|
23
|
-
* This import intentionally uses a module path instead of a package path
|
|
24
|
-
* to let the app override the implementation via tsconfig paths.
|
|
25
|
-
*
|
|
26
|
-
* For more details, see `moduleConfigLoader` in `./src/stub-loader/interface.ts`.
|
|
27
|
-
*
|
|
28
|
-
* Dynamic import is also important here to avoid "cannot acess before initialization" error.
|
|
29
|
-
*/
|
|
30
|
-
const { default: configLoader } = await import("@izumisy-tailor/omakase-modules/config/loader");
|
|
31
|
-
return await factory(await configLoader.loadConfig(module));
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
//#endregion
|
|
35
|
-
export { defineModule, withModuleConfiguration };
|
package/dist/config/index.d.mts
DELETED