@frigear-nu/module-kit 0.0.2

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 ADDED
@@ -0,0 +1,41 @@
1
+ # @frigar-nu/module-kit
2
+
3
+ A simple nuxt module to simplify development of other nuxt modules within frigear.
4
+
5
+ ## Purpose
6
+
7
+ - Be a shared module kit for building nuxt modules within frigear – as generic as possible within the frigear requirements
8
+ - E.g building a 'user' or 'blog' or 'post' module for frigear-nu
9
+
10
+ ## Extending a database table
11
+
12
+ Modules can register a table schema using `extendDatabaseTable` from `@frigear-nu/module-kit/utils`:
13
+
14
+ ```ts
15
+ extendDatabaseTable(nuxt, {
16
+ name: 'users',
17
+ factory: 'defineUsersTable',
18
+ resolver: createResolver(import.meta.url),
19
+ schema: dialect => `./runtime/db/schema.${dialect}`,
20
+ override: options.settings?.schemas?.users,
21
+ })
22
+ ```
23
+
24
+ The schema file exports the named factory, which accepts an object of additional columns and returns a table. The factory must spread the received `extra` columns *last* in the object passed to the table builder (after every one of its own fields, including timestamps), so extended columns always end up last in the generated migration, matching the order they were registered in. Other modules can register column files during setup:
25
+
26
+ ```ts
27
+ registerDatabaseTableExtension(nuxt, {
28
+ name: 'users',
29
+ resolver: createResolver(import.meta.url),
30
+ file: dialect => `./runtime/db/extra.${dialect}`,
31
+ })
32
+ ```
33
+
34
+ Each extension file exports column objects named for their tables. For example, a Nuxt layer can extend several tables in `server/db/extend.sqlite.ts`:
35
+
36
+ ```ts
37
+ export const users = defineExtendedColumns({ locale: text().default('da') })
38
+ export const posts = defineExtendedColumns({ summary: text() })
39
+ ```
40
+
41
+ Only the export matching the table being built is used. Existing registered files are merged in registration order (later fields replace earlier fields); missing files for a dialect are skipped. Registered files with legacy default exports still work. Nuxt layer files are merged next, from base layers toward the host application. Older `server/db/<name>.extend.<dialect>` files with default exports are also loaded before each layer's shared file, so a shared export can override them. Pass `extensionFile: dialect => 'path/to/columns.' + dialect + '.ts'` to change the per-layer file location. If `override` is a path instead of `'default'`, the host schema replaces the generated table entirely; the path is resolved from the application's root directory.
@@ -0,0 +1,10 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+ export * from '../dist/runtime/utils/index.js';
3
+
4
+ interface ModuleOptions {
5
+ _modules?: string[];
6
+ }
7
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
8
+
9
+ export { _default as default };
10
+ export type { ModuleOptions };
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@frigear-nu/module-kit",
3
+ "configKey": "moduleKit",
4
+ "compatibility": {
5
+ "nuxt": ">=4.0.0"
6
+ },
7
+ "version": "0.0.2",
8
+ "builder": {
9
+ "@nuxt/module-builder": "1.0.3",
10
+ "unbuild": "unknown"
11
+ }
12
+ }
@@ -0,0 +1,28 @@
1
+ import { defineNuxtModule, createResolver } from '@nuxt/kit';
2
+ export * from '../dist/runtime/utils/index.js';
3
+
4
+ const module$1 = defineNuxtModule({
5
+ meta: {
6
+ name: "@frigear-nu/module-kit",
7
+ configKey: "moduleKit",
8
+ compatibility: {
9
+ nuxt: ">=4.0.0"
10
+ }
11
+ },
12
+ // Default configuration options of the Nuxt module
13
+ defaults: {
14
+ _modules: []
15
+ },
16
+ setup(_options, _nuxt) {
17
+ createResolver(import.meta.url);
18
+ _nuxt.options.moduleKit ||= {};
19
+ _nuxt.options.moduleKit._modules = [
20
+ .../* @__PURE__ */ new Set([
21
+ ..._nuxt.options.moduleKit._modules ?? [],
22
+ ..._options._modules ?? []
23
+ ])
24
+ ];
25
+ }
26
+ });
27
+
28
+ export { module$1 as default };
@@ -0,0 +1,6 @@
1
+ export * from './manifest.js';
2
+ export * from './pages.js';
3
+ export * from './template.js';
4
+ export * from './server.js';
5
+ export * from './module.js';
6
+ export * from './url.js';
@@ -0,0 +1,6 @@
1
+ export * from "./manifest.js";
2
+ export * from "./pages.js";
3
+ export * from "./template.js";
4
+ export * from "./server.js";
5
+ export * from "./module.js";
6
+ export * from "./url.js";
@@ -0,0 +1,2 @@
1
+ import { type Resolver } from '@nuxt/kit';
2
+ export declare const addModuleManifest: (moduleName: string, _resolver: Resolver) => void;
@@ -0,0 +1,10 @@
1
+ import { addTemplate } from "@nuxt/kit";
2
+ export const addModuleManifest = (moduleName, _resolver) => {
3
+ addTemplate({
4
+ filename: `${moduleName}.json`,
5
+ write: true,
6
+ getContents: () => {
7
+ return `export default ${JSON.stringify({ moduleName }, null, 2)}`;
8
+ }
9
+ });
10
+ };
@@ -0,0 +1,2 @@
1
+ import type { Nuxt } from '@nuxt/schema';
2
+ export declare const registerModule: (moduleName: string, nuxt: Nuxt) => void;
@@ -0,0 +1,8 @@
1
+ export const registerModule = (moduleName, nuxt) => {
2
+ nuxt.options.moduleKit ||= {};
3
+ const registeredModules = Array.isArray(nuxt.options.moduleKit._modules) ? nuxt.options.moduleKit._modules : [];
4
+ nuxt.options.moduleKit._modules = registeredModules;
5
+ if (!registeredModules.includes(moduleName)) {
6
+ registeredModules.push(moduleName);
7
+ }
8
+ };
@@ -0,0 +1,2 @@
1
+ import type { NuxtPage } from '@nuxt/schema';
2
+ export declare const defineModulePage: (moduleName: string, page: NuxtPage) => NuxtPage;
@@ -0,0 +1,9 @@
1
+ export const defineModulePage = (moduleName, page) => {
2
+ return {
3
+ ...page,
4
+ meta: {
5
+ moduleName,
6
+ ...page.meta
7
+ }
8
+ };
9
+ };
@@ -0,0 +1,36 @@
1
+ import { createResolver } from '@nuxt/kit';
2
+ import type { Nuxt } from '@nuxt/schema';
3
+ import type { ColumnBuilderBase } from 'drizzle-orm';
4
+ type DbExtenderFn = (props: {
5
+ dialect: string;
6
+ paths: string[];
7
+ }) => void | Promise<void>;
8
+ type TableExtenderFn = (props: {
9
+ name: string;
10
+ dialect: string;
11
+ paths: string[];
12
+ }) => void | Promise<void>;
13
+ declare module '@nuxt/schema' {
14
+ interface NuxtHooks {
15
+ 'hub:db:schema:extend': DbExtenderFn;
16
+ 'module-kit:db:table:extend': TableExtenderFn;
17
+ }
18
+ }
19
+ export declare const defineExtendedColumns: <T extends Record<string, ColumnBuilderBase>>(columns: T) => T;
20
+ export declare const extendDatabaseSchema: (nuxt: Nuxt, extender: DbExtenderFn) => Promise<() => void>;
21
+ type DatabaseTableOptions = {
22
+ name: string;
23
+ factory: string;
24
+ resolver: ReturnType<typeof createResolver>;
25
+ schema: (dialect: string) => string;
26
+ override?: string;
27
+ extensionFile?: (dialect: string) => string;
28
+ };
29
+ type TableExtensionOptions = {
30
+ name: string;
31
+ resolver: ReturnType<typeof createResolver>;
32
+ file: (dialect: string) => string;
33
+ };
34
+ export declare const registerDatabaseTableExtension: (nuxt: Nuxt, options: TableExtensionOptions) => void;
35
+ export declare const extendDatabaseTable: (nuxt: Nuxt, options: DatabaseTableOptions) => Promise<() => void>;
36
+ export {};
@@ -0,0 +1,48 @@
1
+ import { addTemplate, createResolver } from "@nuxt/kit";
2
+ import { existsSync } from "node:fs";
3
+ export const defineExtendedColumns = (columns) => {
4
+ return columns;
5
+ };
6
+ export const extendDatabaseSchema = async (nuxt, extender) => {
7
+ return nuxt.hook("hub:db:schema:extend", extender);
8
+ };
9
+ export const registerDatabaseTableExtension = (nuxt, options) => {
10
+ nuxt.hook("module-kit:db:table:extend", async ({ name, dialect, paths }) => {
11
+ if (name === options.name) {
12
+ const file = await options.resolver.resolvePath(options.file(dialect));
13
+ if (existsSync(file)) paths.push(file);
14
+ }
15
+ });
16
+ };
17
+ export const extendDatabaseTable = (nuxt, options) => {
18
+ const { name, factory, resolver, schema, override, extensionFile } = options;
19
+ return extendDatabaseSchema(nuxt, async ({ dialect, paths }) => {
20
+ if (override && override !== "default") {
21
+ paths.push(await resolver.resolvePath(override, { cwd: nuxt.options.rootDir }));
22
+ return;
23
+ }
24
+ const registered = [];
25
+ await nuxt.callHook("module-kit:db:table:extend", { name, dialect, paths: registered });
26
+ const extensions = registered.map((file) => ({ file, legacy: true }));
27
+ const layerRoots = [...new Set(nuxt.options._layers?.map((layer) => layer.config.rootDir).reverse() ?? [])];
28
+ for (const root of [...layerRoots.filter((root2) => root2 !== nuxt.options.rootDir), nuxt.options.rootDir]) {
29
+ const rootResolver = createResolver(root);
30
+ const extensionPath = await rootResolver.resolvePath(extensionFile?.(dialect) ?? `server/db/extend.${dialect}.ts`);
31
+ if (!extensionFile) {
32
+ const legacyPath = await rootResolver.resolvePath(`server/db/${name}.extend.${dialect}`);
33
+ if (existsSync(legacyPath)) extensions.push({ file: legacyPath, legacy: true });
34
+ }
35
+ if (existsSync(extensionPath)) extensions.push({ file: extensionPath, legacy: false });
36
+ }
37
+ const template = addTemplate({
38
+ filename: `${name}/schema.${dialect}.ts`,
39
+ write: true,
40
+ getContents: () => `
41
+ import { ${factory} } from ${JSON.stringify(resolver.resolve(schema(dialect)))}
42
+ ${extensions.map(({ file }, index) => `import * as extra${index} from ${JSON.stringify(file)}`).join("\n")}
43
+ export const ${name} = ${factory}({ ${extensions.map(({ legacy }, index) => legacy ? `...(extra${index}[${JSON.stringify(name)} as keyof typeof extra${index}] ?? Object.entries(extra${index}).find(([key]) => key === 'default')?.[1] ?? {})` : `...(extra${index}[${JSON.stringify(name)} as keyof typeof extra${index}] ?? {})`).join(", ")} })
44
+ `
45
+ });
46
+ paths.push(template.dst);
47
+ });
48
+ };
@@ -0,0 +1 @@
1
+ export declare const buildUseModuleInfoTemplate: () => void;
@@ -0,0 +1,2 @@
1
+ export const buildUseModuleInfoTemplate = () => {
2
+ };
@@ -0,0 +1,2 @@
1
+ export declare const moduleUrl: (moduleName: string, path?: string) => string;
2
+ export declare const moduleApiUrl: (moduleName: string, path?: string) => string;
@@ -0,0 +1,15 @@
1
+ import { kebabCase } from "scule";
2
+ import { joinURL, withLeadingSlash } from "ufo";
3
+ export const moduleUrl = (moduleName, path) => {
4
+ return withLeadingSlash(joinURL(
5
+ path?.startsWith("/") ? "" : kebabCase(moduleName),
6
+ path ?? ""
7
+ ));
8
+ };
9
+ export const moduleApiUrl = (moduleName, path) => {
10
+ return withLeadingSlash(joinURL(
11
+ "api",
12
+ path?.startsWith("/") ? "" : kebabCase(moduleName),
13
+ path ?? ""
14
+ ));
15
+ };
@@ -0,0 +1,5 @@
1
+ export { default } from './module.mjs'
2
+
3
+ export { type ModuleOptions } from './module.mjs'
4
+
5
+ export * from '../dist/runtime/utils/index.js'
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@frigear-nu/module-kit",
3
+ "version": "0.0.2",
4
+ "type": "module",
5
+ "private": false,
6
+ "description": "Frigear Module Kit",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/frigear-nu/frigear-nu.git",
10
+ "directory": "packages/module-kit"
11
+ },
12
+ "license": "MIT",
13
+ "main": "./dist/module.mjs",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/types.d.mts",
17
+ "import": "./dist/module.mjs",
18
+ "default": "./dist/module.mjs"
19
+ },
20
+ "./utils": {
21
+ "types": "./dist/runtime/utils/index.d.ts",
22
+ "import": "./dist/runtime/utils/index.js",
23
+ "default": "./dist/runtime/utils/index.js"
24
+ }
25
+ },
26
+ "typesVersions": {
27
+ "*": {
28
+ ".": [
29
+ "./dist/types.d.mts"
30
+ ]
31
+ }
32
+ },
33
+ "files": [
34
+ "dist"
35
+ ],
36
+ "workspaces": [
37
+ "playground"
38
+ ],
39
+ "dependencies": {
40
+ "@nuxt/kit": "^4.5.2",
41
+ "defu": "^6.1.7",
42
+ "scule": "^1.3.0",
43
+ "ufo": "^1.6.4",
44
+ "zod": "^4.6.5"
45
+ },
46
+ "devDependencies": {
47
+ "@nuxt/devtools": "^3.4.2",
48
+ "@nuxt/eslint-config": "^1.17.0",
49
+ "@nuxt/module-builder": "^1.0.3",
50
+ "@nuxt/schema": "^4.5.2",
51
+ "@nuxt/test-utils": "^4.3.2",
52
+ "@types/node": "latest",
53
+ "changelogen": "^0.6.2",
54
+ "eslint": "^10.10.0",
55
+ "nuxt": "^4.5.2",
56
+ "typescript": "^6.0.3",
57
+ "vitest": "^5.0.1",
58
+ "vue-tsc": "^3.3.11"
59
+ },
60
+ "peerDependencies": {
61
+ "drizzle-orm": "^0.45.3",
62
+ "drizzle-kit": "^0.31.11"
63
+ },
64
+ "scripts": {
65
+ "postinstall": "nuxt-module-build build --stub && nuxt prepare && nuxt-module-build build",
66
+ "build": "nuxt-module-build build",
67
+ "dev": "npm run dev:prepare && nuxt dev playground",
68
+ "dev:build": "nuxt build playground",
69
+ "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare playground",
70
+ "release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
71
+ "lint": "eslint .",
72
+ "lint:fix": "eslint . --fix",
73
+ "test": "vitest run",
74
+ "test:watch": "vitest watch",
75
+ "test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
76
+ }
77
+ }