@monkeyplus/flow 6.0.12 → 6.0.14
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 +5 -0
- package/modules/content/module.mjs +12 -0
- package/modules/content/query.d.ts +1 -1
- package/modules/content/query.mjs +157 -57
- package/modules/netlify-cms/handler.d.ts +2 -0
- package/modules/netlify-cms/handler.mjs +5 -0
- package/modules/netlify-cms/module.d.ts +6 -0
- package/modules/netlify-cms/module.mjs +66 -0
- package/modules/netlify-cms/resources/admin.html +16 -0
- package/modules/netlify-cms/resources/adminV2.html +15 -0
- package/modules/netlify-cms/runtime/cms.d.ts +3 -0
- package/modules/netlify-cms/runtime/cms.mjs +3 -0
- package/modules/netlify-cms/server/api/admin.d.ts +2 -0
- package/modules/netlify-cms/server/api/admin.mjs +20 -0
- package/modules/netlify-cms/server/api/config.d.ts +2 -0
- package/modules/netlify-cms/server/api/config.mjs +72 -0
- package/modules/netlify-cms/server/api/local-fs.d.ts +2 -0
- package/modules/netlify-cms/server/api/local-fs.mjs +189 -0
- package/modules/netlify-cms/server/api/meta.d.ts +2 -0
- package/modules/netlify-cms/server/api/meta.mjs +8 -0
- package/modules/netlify-cms/server/lib/cms/handler.d.ts +2 -0
- package/modules/netlify-cms/server/lib/cms/handler.mjs +37 -0
- package/modules/netlify-cms/server/lib/cms/handlerV1.d.ts +2 -0
- package/modules/netlify-cms/server/lib/cms/handlerV1.mjs +40 -0
- package/modules/netlify-cms/server/lib/cms/helpers.d.ts +14 -0
- package/modules/netlify-cms/server/lib/cms/helpers.mjs +76 -0
- package/modules/netlify-cms/server/lib/cms/widgets.d.ts +113 -0
- package/modules/netlify-cms/server/lib/cms/widgets.mjs +168 -0
- package/modules/netlify-cms/server/lib/composables.d.ts +28 -0
- package/modules/netlify-cms/server/lib/composables.mjs +14 -0
- package/modules/netlify-cms/server/lib/entries.d.ts +25 -0
- package/modules/netlify-cms/server/lib/entries.mjs +39 -0
- package/modules/netlify-cms/server/lib/fs.d.ts +5 -0
- package/modules/netlify-cms/server/lib/fs.mjs +43 -0
- package/modules/netlify-cms/server/lib/types/collections.d.ts +16 -0
- package/modules/netlify-cms/server/lib/types/collections.mjs +0 -0
- package/modules/netlify-cms/server/lib/types/helpers.d.ts +23 -0
- package/modules/netlify-cms/server/lib/types/helpers.mjs +0 -0
- package/modules/netlify-cms/server/lib/types/index.d.ts +23 -0
- package/modules/netlify-cms/server/lib/types/index.mjs +11 -0
- package/modules/netlify-cms/server/lib/types/widgets.d.ts +39 -0
- package/modules/netlify-cms/server/lib/types/widgets.mjs +0 -0
- package/package.json +1 -1
- package/server/lib/handler.mjs +2 -2
- package/server/lib/pages.mjs +24 -3
- package/server/renderer.mjs +4 -0
- package/src/public/nitro.mjs +2 -0
- package/src/public/query-content.d.ts +8 -0
- package/src/public/query-content.mjs +103 -37
- package/src/runtime/config.d.ts +7 -0
- package/src/runtime/modules.mjs +2 -1
- package/src/runtime/nitro-plugin.d.ts +1 -0
- package/src/runtime/nitro-plugin.mjs +5 -0
- package/src/runtime/page-discovery.mjs +24 -3
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
export function defineCmsCollection(options) {
|
|
3
|
+
return (ctx) => {
|
|
4
|
+
const setupFn = options.setup(ctx);
|
|
5
|
+
const collectionFn = setupFn(join(options.group, options.dir));
|
|
6
|
+
return (baseDir, ctx2) => {
|
|
7
|
+
const collection = collectionFn(baseDir, ctx2);
|
|
8
|
+
return {
|
|
9
|
+
...collection,
|
|
10
|
+
group: options.group
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare function entriesFromFiles(repoPath: string, files: {
|
|
2
|
+
path: string;
|
|
3
|
+
label?: string;
|
|
4
|
+
}[]): Promise<({
|
|
5
|
+
data: string;
|
|
6
|
+
file: {
|
|
7
|
+
path: string;
|
|
8
|
+
label: string | undefined;
|
|
9
|
+
id: any;
|
|
10
|
+
};
|
|
11
|
+
} | {
|
|
12
|
+
data: null;
|
|
13
|
+
file: {
|
|
14
|
+
path: string;
|
|
15
|
+
label: string | undefined;
|
|
16
|
+
id: null;
|
|
17
|
+
};
|
|
18
|
+
})[]>;
|
|
19
|
+
export declare function readMediaFile(repoPath: string, file: string): Promise<{
|
|
20
|
+
id: any;
|
|
21
|
+
content: string;
|
|
22
|
+
encoding: string;
|
|
23
|
+
path: string;
|
|
24
|
+
name: any;
|
|
25
|
+
}>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
function sha256(buffer) {
|
|
5
|
+
return crypto.createHash("sha256").update(buffer).digest("hex");
|
|
6
|
+
}
|
|
7
|
+
function normalizePath(path2) {
|
|
8
|
+
return path2.replace(/\\/g, "/");
|
|
9
|
+
}
|
|
10
|
+
export async function entriesFromFiles(repoPath, files) {
|
|
11
|
+
return Promise.all(
|
|
12
|
+
files.map(async (file) => {
|
|
13
|
+
try {
|
|
14
|
+
const content = await fs.readFile(path.join(repoPath, file.path));
|
|
15
|
+
return {
|
|
16
|
+
data: content.toString(),
|
|
17
|
+
file: { path: normalizePath(file.path), label: file.label, id: sha256(content) }
|
|
18
|
+
};
|
|
19
|
+
} catch (e) {
|
|
20
|
+
return {
|
|
21
|
+
data: null,
|
|
22
|
+
file: { path: normalizePath(file.path), label: file.label, id: null }
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
export async function readMediaFile(repoPath, file) {
|
|
29
|
+
const encoding = "base64";
|
|
30
|
+
const buffer = await fs.readFile(path.join(repoPath, file));
|
|
31
|
+
const id = sha256(buffer);
|
|
32
|
+
return {
|
|
33
|
+
id,
|
|
34
|
+
content: buffer.toString(encoding),
|
|
35
|
+
encoding,
|
|
36
|
+
path: normalizePath(file),
|
|
37
|
+
name: path.basename(file)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function listRepoFiles(repoPath: string, folder: string, extension: string, depth: number): Promise<string[]>;
|
|
2
|
+
export declare function writeFile(filePath: string, content: Buffer | string): Promise<void>;
|
|
3
|
+
export declare function deleteFile(repoPath: string, filePath: string): Promise<void>;
|
|
4
|
+
export declare function move(from: string, to: string): Promise<void>;
|
|
5
|
+
export declare function getUpdateDate(repoPath: string, filePath: string): Promise<Date>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
async function listFiles(dir, extension, depth) {
|
|
4
|
+
if (depth <= 0)
|
|
5
|
+
return [];
|
|
6
|
+
try {
|
|
7
|
+
const dirents = await fs.readdir(dir, { withFileTypes: true });
|
|
8
|
+
const files = await Promise.all(
|
|
9
|
+
dirents.map((dirent) => {
|
|
10
|
+
const res = path.join(dir, dirent.name);
|
|
11
|
+
return dirent.isDirectory() ? listFiles(res, extension, depth - 1) : [res].filter((f) => f.endsWith(extension));
|
|
12
|
+
})
|
|
13
|
+
);
|
|
14
|
+
return [].concat(...files);
|
|
15
|
+
} catch (e) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export async function listRepoFiles(repoPath, folder, extension, depth) {
|
|
20
|
+
const files = await listFiles(path.join(repoPath, folder), extension, depth);
|
|
21
|
+
return files.map((f) => f.substr(repoPath.length + 1));
|
|
22
|
+
}
|
|
23
|
+
export async function writeFile(filePath, content) {
|
|
24
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
25
|
+
await fs.writeFile(filePath, content);
|
|
26
|
+
}
|
|
27
|
+
export async function deleteFile(repoPath, filePath) {
|
|
28
|
+
await fs.unlink(path.join(repoPath, filePath)).catch(() => void 0);
|
|
29
|
+
}
|
|
30
|
+
async function moveFile(from, to) {
|
|
31
|
+
await fs.mkdir(path.dirname(to), { recursive: true });
|
|
32
|
+
await fs.rename(from, to);
|
|
33
|
+
}
|
|
34
|
+
export async function move(from, to) {
|
|
35
|
+
await moveFile(from, to);
|
|
36
|
+
const sourceDir = path.dirname(from);
|
|
37
|
+
const destDir = path.dirname(to);
|
|
38
|
+
const allFiles = await listFiles(sourceDir, "", 100);
|
|
39
|
+
await Promise.all(allFiles.map((file) => moveFile(file, file.replace(sourceDir, destDir))));
|
|
40
|
+
}
|
|
41
|
+
export async function getUpdateDate(repoPath, filePath) {
|
|
42
|
+
return fs.stat(path.join(repoPath, filePath)).then((stat) => stat.mtime).catch(() => /* @__PURE__ */ new Date());
|
|
43
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { CmsCollection, CmsCollectionFile } from 'netlify-cms-core';
|
|
2
|
+
import type { Cms } from './index';
|
|
3
|
+
export declare namespace Collections {
|
|
4
|
+
type _Base = Omit<CmsCollection, 'files' | 'folder' | 'fields'>;
|
|
5
|
+
type Folder = _Base & {
|
|
6
|
+
folder: string;
|
|
7
|
+
fields: Cms.Fields[];
|
|
8
|
+
};
|
|
9
|
+
type Files = _Base & {
|
|
10
|
+
files: File[];
|
|
11
|
+
};
|
|
12
|
+
type File = Omit<CmsCollectionFile, 'fields'> & {
|
|
13
|
+
fields: Cms.Fields[];
|
|
14
|
+
htmlId?: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Collections } from './collections';
|
|
2
|
+
import type { Cms } from './index';
|
|
3
|
+
export type DefineMethod<R> = (parentName: string, ctx: ContextWidget) => R;
|
|
4
|
+
export type DefineMethodExtend<R> = (id: string) => (parentName: string, ctx: ContextWidget) => R;
|
|
5
|
+
export interface ContextWidget {
|
|
6
|
+
rootDir: string;
|
|
7
|
+
}
|
|
8
|
+
export type DefineFile = Record<string, DefineMethodExtend<Collections.File>>;
|
|
9
|
+
export interface CollectionFileId extends Omit<Collections._Base, 'name'> {
|
|
10
|
+
files: DefineFile;
|
|
11
|
+
}
|
|
12
|
+
export interface CollectionFolderId extends Omit<Collections.Folder, 'name' | 'folder' | 'fields'> {
|
|
13
|
+
fields: Model.Fields;
|
|
14
|
+
}
|
|
15
|
+
export declare namespace Model {
|
|
16
|
+
type Fields = Record<string, (parentName: string, ctx: ContextWidget) => Cms.Fields>;
|
|
17
|
+
}
|
|
18
|
+
export declare namespace Helpers {
|
|
19
|
+
interface CollectionFiles {
|
|
20
|
+
(base: CollectionFileId): (baseDir: string) => DefineMethod<Collections.Files>;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export type DefineCms = (options: Omit<Cms.Config, 'collections'>, collections: DefineMethod<Collections.Files | Collections.Folder>[]) => (dir: string, ctx: ContextWidget) => Cms.Config;
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Collections } from './collections';
|
|
2
|
+
import type { DefineMethod } from './helpers';
|
|
3
|
+
import type { Widgets } from './widgets';
|
|
4
|
+
export { Collections } from './collections';
|
|
5
|
+
export { ContextWidget, DefineCms } from './helpers';
|
|
6
|
+
export { CollectionFileId, CollectionFolderId, DefineMethod, DefineMethodExtend, Helpers, Model, } from './helpers';
|
|
7
|
+
export { Widgets } from './widgets';
|
|
8
|
+
export declare namespace Cms {
|
|
9
|
+
type Config = Record<string, any> & {
|
|
10
|
+
collections: (Collections.Folder | Collections.Files)[];
|
|
11
|
+
};
|
|
12
|
+
type Fields = Widgets.Boolean | Widgets.Markdown | Widgets.Code | Widgets.Color | Widgets.DateTime | Widgets.Hidden | Widgets.Map | Widgets.Number | Widgets.Select | Widgets.Text | Widgets.String | Widgets.Relation | Widgets.File | Widgets.Image | Widgets.Object | Widgets.List | Widgets.Date | Widgets.Meta;
|
|
13
|
+
}
|
|
14
|
+
export interface FlowNetlifyCms {
|
|
15
|
+
config?: Omit<Partial<Cms.Config>, 'collections'>;
|
|
16
|
+
functions?: {
|
|
17
|
+
dir: string;
|
|
18
|
+
proxy?: Record<string, string>;
|
|
19
|
+
};
|
|
20
|
+
omitRoutes?: boolean;
|
|
21
|
+
omitProxy?: boolean;
|
|
22
|
+
collections?: DefineMethod<Collections.Files | Collections.Folder>[];
|
|
23
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { Collections } from "./collections.mjs";
|
|
2
|
+
export { ContextWidget, DefineCms } from "./helpers.mjs";
|
|
3
|
+
export {
|
|
4
|
+
CollectionFileId,
|
|
5
|
+
CollectionFolderId,
|
|
6
|
+
DefineMethod,
|
|
7
|
+
DefineMethodExtend,
|
|
8
|
+
Helpers,
|
|
9
|
+
Model
|
|
10
|
+
} from "./helpers.mjs";
|
|
11
|
+
export { Widgets } from "./widgets.mjs";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { CmsFieldBase, CmsFieldBoolean, CmsFieldCode, CmsFieldColor, CmsFieldDateTime, CmsFieldFileOrImage, CmsFieldHidden, CmsFieldList, CmsFieldMap, CmsFieldMarkdown, CmsFieldMeta, CmsFieldNumber, CmsFieldObject, CmsFieldRelation, CmsFieldSelect, CmsFieldStringOrText } from 'netlify-cms-core';
|
|
2
|
+
import type { Cms } from './index';
|
|
3
|
+
interface CmsFieldExtra {
|
|
4
|
+
extraOptions?: Record<string, any>;
|
|
5
|
+
htmlId?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare namespace Widgets {
|
|
8
|
+
type Boolean = CmsFieldExtra & CmsFieldBase & CmsFieldBoolean;
|
|
9
|
+
type Markdown = CmsFieldExtra & CmsFieldBase & CmsFieldMarkdown;
|
|
10
|
+
type Code = CmsFieldExtra & CmsFieldBase & CmsFieldCode;
|
|
11
|
+
type Color = CmsFieldExtra & CmsFieldBase & CmsFieldColor;
|
|
12
|
+
type DateTime = CmsFieldExtra & CmsFieldBase & CmsFieldDateTime;
|
|
13
|
+
type Hidden = CmsFieldExtra & CmsFieldBase & CmsFieldHidden;
|
|
14
|
+
type Map = CmsFieldExtra & CmsFieldBase & CmsFieldMap;
|
|
15
|
+
type Number = CmsFieldExtra & CmsFieldBase & CmsFieldNumber;
|
|
16
|
+
type Select = CmsFieldExtra & CmsFieldBase & CmsFieldSelect;
|
|
17
|
+
type Text = CmsFieldExtra & CmsFieldBase & CmsFieldStringOrText;
|
|
18
|
+
type String = CmsFieldExtra & CmsFieldBase & CmsFieldStringOrText;
|
|
19
|
+
type Relation = CmsFieldExtra & CmsFieldBase & CmsFieldRelation;
|
|
20
|
+
type File = CmsFieldExtra & CmsFieldBase & CmsFieldFileOrImage;
|
|
21
|
+
type Image = CmsFieldExtra & CmsFieldBase & CmsFieldFileOrImage;
|
|
22
|
+
type Meta = CmsFieldExtra & CmsFieldBase & CmsFieldMeta & {
|
|
23
|
+
options?: Record<string, unknown>;
|
|
24
|
+
};
|
|
25
|
+
type Object = CmsFieldExtra & CmsFieldBase & Omit<CmsFieldObject, 'fields'> & {
|
|
26
|
+
fields?: Cms.Fields[];
|
|
27
|
+
};
|
|
28
|
+
type List = CmsFieldExtra & CmsFieldBase & Omit<CmsFieldList, 'fields' | 'field'> & {
|
|
29
|
+
field?: Cms.Fields;
|
|
30
|
+
fields?: Cms.Fields[];
|
|
31
|
+
};
|
|
32
|
+
type Date = CmsFieldExtra & CmsFieldBase & {
|
|
33
|
+
widget: 'date';
|
|
34
|
+
format?: string;
|
|
35
|
+
date_format?: boolean | string;
|
|
36
|
+
time_format?: boolean | string;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export {};
|
|
File without changes
|
package/package.json
CHANGED
package/server/lib/handler.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import clientAssets from "../../src/main.mjs?assets=client";
|
|
2
1
|
import bundleAssets from "virtual:flow/client-page-assets";
|
|
3
|
-
import
|
|
2
|
+
import clientAssets from "../../src/main.mjs?assets=client";
|
|
4
3
|
import { resolvePage, resolvePageByName } from "./pages.mjs";
|
|
4
|
+
import { renderDocument } from "./render.mjs";
|
|
5
5
|
function mergeClientAssets(baseAssets, pageAssets) {
|
|
6
6
|
const css = [...baseAssets.css || [], ...pageAssets?.css || []];
|
|
7
7
|
const js = [...baseAssets.js || [], ...pageAssets?.js || []];
|
package/server/lib/pages.mjs
CHANGED
|
@@ -130,6 +130,26 @@ function replacePath(pattern, url) {
|
|
|
130
130
|
function combineName(name, dynamicName) {
|
|
131
131
|
return `${name.replace(/\/*$/, "")}/${dynamicName.replace(/^\/*/, "")}`;
|
|
132
132
|
}
|
|
133
|
+
function resolveUrlTarget(namePage, explicitDynamicName) {
|
|
134
|
+
if (explicitDynamicName || !namePage.includes("/")) {
|
|
135
|
+
return {
|
|
136
|
+
namePage,
|
|
137
|
+
dynamicName: explicitDynamicName
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const match = pageDefinitions.filter((candidate) => candidate.name && namePage.startsWith(`${candidate.name}/`)).sort((left, right) => right.name.length - left.name.length)[0];
|
|
141
|
+
if (!match) {
|
|
142
|
+
return {
|
|
143
|
+
namePage,
|
|
144
|
+
dynamicName: explicitDynamicName
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const dynamicName = namePage.slice(match.name.length + 1);
|
|
148
|
+
return {
|
|
149
|
+
namePage: match.name,
|
|
150
|
+
dynamicName: dynamicName || explicitDynamicName
|
|
151
|
+
};
|
|
152
|
+
}
|
|
133
153
|
function getEnabledLocaleCodes() {
|
|
134
154
|
const runtimeConfig = getFlowRuntimeConfig();
|
|
135
155
|
return runtimeConfig.flow?.locale?.locales || [];
|
|
@@ -163,8 +183,9 @@ export async function getUrl(namePage, localeCode, options = {}) {
|
|
|
163
183
|
const enabledLocales = getEnabledLocaleCodes();
|
|
164
184
|
const targetLocale = localeCode || enabledLocales[0];
|
|
165
185
|
const runtimeConfig = getFlowRuntimeConfig();
|
|
186
|
+
const target = resolveUrlTarget(namePage, options.dynamicName);
|
|
166
187
|
for (const definition of pageDefinitions) {
|
|
167
|
-
if (definition.name !== namePage) {
|
|
188
|
+
if (definition.name !== target.namePage) {
|
|
168
189
|
continue;
|
|
169
190
|
}
|
|
170
191
|
const localeEntries = targetLocale ? [targetLocale] : Object.keys(definition.locales);
|
|
@@ -180,11 +201,11 @@ export async function getUrl(namePage, localeCode, options = {}) {
|
|
|
180
201
|
if (options.params) {
|
|
181
202
|
return replacePath(localizedRoute, options.params);
|
|
182
203
|
}
|
|
183
|
-
if (localePage.dynamic &&
|
|
204
|
+
if (localePage.dynamic && target.dynamicName) {
|
|
184
205
|
const locale = createLocale(code);
|
|
185
206
|
const ctx = createContext(definition, locale, localePage, toPublicRoute(runtimeConfig.flow, code, localePage.url), {});
|
|
186
207
|
const entries = await getDynamicEntries(definition, code, ctx);
|
|
187
|
-
const entry = entries.find((candidate) => candidate.name ===
|
|
208
|
+
const entry = entries.find((candidate) => candidate.name === target.dynamicName);
|
|
188
209
|
if (!entry) {
|
|
189
210
|
return void 0;
|
|
190
211
|
}
|
package/server/renderer.mjs
CHANGED
|
@@ -36,6 +36,10 @@ async function resolvePrerenderFetchHandler() {
|
|
|
36
36
|
return await prerenderFetchHandlerPromise;
|
|
37
37
|
}
|
|
38
38
|
export default async function renderPage(input) {
|
|
39
|
+
const nitroApp = globalThis.__nitro__;
|
|
40
|
+
if (nitroApp && nitroApp.localFetch) {
|
|
41
|
+
globalThis.$fetch = nitroApp.localFetch;
|
|
42
|
+
}
|
|
39
43
|
const viteEnvs = globalThis.__nitro_vite_envs__;
|
|
40
44
|
const request = resolveRequest(input);
|
|
41
45
|
const viteEnv = viteEnvs?.ssr;
|
package/src/public/nitro.mjs
CHANGED
|
@@ -64,10 +64,12 @@ export function createFlowNitroConfig(options = {}) {
|
|
|
64
64
|
plugins: flowModules.nitro.plugins,
|
|
65
65
|
noExternals: [...configuredNoExternals, flowPackagePattern],
|
|
66
66
|
handlers: flowModules.nitro.handlers,
|
|
67
|
+
storage: flowModules.nitro.storage,
|
|
67
68
|
routeRules: {
|
|
68
69
|
"/api/**": { cors: true },
|
|
69
70
|
...flowModules.nitro.routeRules
|
|
70
71
|
},
|
|
72
|
+
imports: flowModules.nitro.imports,
|
|
71
73
|
runtimeConfig: {
|
|
72
74
|
app: {
|
|
73
75
|
name: "Flow Next",
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import type { ContentEntry, ContentTreeNode } from '../../modules/content/query.ts';
|
|
2
2
|
export interface QueryContentBuilder {
|
|
3
|
+
where: (field: string, operator: string, value?: unknown) => QueryContentBuilder;
|
|
4
|
+
order: (field: string, direction: 'ASC' | 'DESC' | 'asc' | 'desc') => QueryContentBuilder;
|
|
5
|
+
skip: (amount: number) => QueryContentBuilder;
|
|
6
|
+
limit: (amount: number) => QueryContentBuilder;
|
|
7
|
+
select: (...fields: string[]) => QueryContentBuilder;
|
|
3
8
|
find: () => Promise<ContentEntry[]>;
|
|
4
9
|
findOne: () => Promise<ContentEntry | null>;
|
|
10
|
+
all: () => Promise<ContentEntry[]>;
|
|
11
|
+
first: () => Promise<ContentEntry | null>;
|
|
12
|
+
surround: (targetPath: string) => Promise<[ContentEntry | null, ContentEntry | null]>;
|
|
5
13
|
tree: () => Promise<ContentTreeNode[]>;
|
|
6
14
|
}
|
|
7
15
|
export declare function queryContent(path?: string): QueryContentBuilder;
|
|
@@ -27,6 +27,7 @@ function withQuery(url, params) {
|
|
|
27
27
|
if (!params || Object.keys(params).length === 0) {
|
|
28
28
|
return url;
|
|
29
29
|
}
|
|
30
|
+
const isAbsolute = url.startsWith("http://") || url.startsWith("https://") || url.startsWith("//");
|
|
30
31
|
const origin = typeof window !== "undefined" ? window.location.origin : "http://localhost:3000";
|
|
31
32
|
const target = new URL(url, origin);
|
|
32
33
|
Object.entries(params).forEach(([key, value]) => {
|
|
@@ -35,7 +36,7 @@ function withQuery(url, params) {
|
|
|
35
36
|
}
|
|
36
37
|
target.searchParams.set(key, String(value));
|
|
37
38
|
});
|
|
38
|
-
return target.toString();
|
|
39
|
+
return isAbsolute ? target.toString() : target.pathname + target.search;
|
|
39
40
|
}
|
|
40
41
|
async function getRuntimeConfig() {
|
|
41
42
|
if (typeof window !== "undefined") {
|
|
@@ -65,44 +66,46 @@ async function resolveContentApiBase() {
|
|
|
65
66
|
const runtimeConfig = await getRuntimeConfig();
|
|
66
67
|
return runtimeConfig?.flow?.content?.apiBase || runtimeConfig?.public?.flow?.content?.apiBase || defaultContentConfig.apiBase;
|
|
67
68
|
}
|
|
68
|
-
async function
|
|
69
|
-
const runtimeConfig = await getRuntimeConfig();
|
|
69
|
+
async function fetchContent(route, queryParams) {
|
|
70
70
|
const apiBase = await resolveContentApiBase();
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
const query = { ...queryParams };
|
|
72
|
+
if (query.path === "/") {
|
|
73
|
+
delete query.path;
|
|
73
74
|
}
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
for (const key of Object.keys(query)) {
|
|
76
|
+
if (query[key] === void 0 || query[key] === null) {
|
|
77
|
+
delete query[key];
|
|
78
|
+
}
|
|
76
79
|
}
|
|
77
|
-
return joinUrl("http://localhost:3000", apiBase);
|
|
78
|
-
}
|
|
79
|
-
async function fetchContent(route, path) {
|
|
80
|
-
const apiBase = await resolveContentApiBase();
|
|
81
|
-
const query = path && path !== "/" ? { path } : void 0;
|
|
82
80
|
const localFetch = getGlobalFetch();
|
|
83
|
-
const
|
|
84
|
-
if (typeof window === "undefined"
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
81
|
+
const relativeTarget = withQuery(joinUrl(apiBase, route), query);
|
|
82
|
+
if (typeof window === "undefined") {
|
|
83
|
+
try {
|
|
84
|
+
if (import.meta.env?.SSR || process.server) {
|
|
85
|
+
const { findContentEntries, buildContentTree, findContentTree, readContentEntries } = await import("../../modules/content/query.js");
|
|
86
|
+
let entries = await readContentEntries();
|
|
87
|
+
const isTreeRequest = route.endsWith("/tree") || query.tree === true || query.tree === "true" || query.tree === "1";
|
|
88
|
+
if (isTreeRequest) {
|
|
89
|
+
const tree = buildContentTree(entries);
|
|
90
|
+
return findContentTree(tree, query.path);
|
|
91
|
+
}
|
|
92
|
+
if (query.path) {
|
|
93
|
+
entries = findContentEntries(entries, query.path);
|
|
94
|
+
}
|
|
95
|
+
if (query.where) {
|
|
96
|
+
try {
|
|
97
|
+
const whereConditions = JSON.parse(query.where);
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return entries;
|
|
102
|
+
}
|
|
103
|
+
} catch (e) {
|
|
104
|
+
}
|
|
101
105
|
}
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
const response = await fetch(target, {
|
|
106
|
+
const fetchFn = typeof window === "undefined" ? localFetch || fetch : fetch;
|
|
107
|
+
const targetToFetch = typeof window === "undefined" && localFetch ? relativeTarget : relativeTarget;
|
|
108
|
+
const response = await fetchFn(targetToFetch, {
|
|
106
109
|
headers: {
|
|
107
110
|
accept: "application/json"
|
|
108
111
|
}
|
|
@@ -115,16 +118,79 @@ async function fetchContent(route, path) {
|
|
|
115
118
|
}
|
|
116
119
|
export function queryContent(path = "") {
|
|
117
120
|
const normalizedPath = normalizeQueryPath(path);
|
|
118
|
-
|
|
121
|
+
const queryObj = {
|
|
122
|
+
path: normalizedPath,
|
|
123
|
+
where: [],
|
|
124
|
+
order: [],
|
|
125
|
+
skip: 0,
|
|
126
|
+
limit: 0,
|
|
127
|
+
select: []
|
|
128
|
+
};
|
|
129
|
+
const builder = {
|
|
130
|
+
where(field, operator, value) {
|
|
131
|
+
if (value === void 0) {
|
|
132
|
+
value = operator;
|
|
133
|
+
operator = "=";
|
|
134
|
+
}
|
|
135
|
+
queryObj.where.push({ field, operator, value });
|
|
136
|
+
return this;
|
|
137
|
+
},
|
|
138
|
+
order(field, direction = "ASC") {
|
|
139
|
+
queryObj.order.push({ field, direction });
|
|
140
|
+
return this;
|
|
141
|
+
},
|
|
142
|
+
skip(amount) {
|
|
143
|
+
queryObj.skip = amount;
|
|
144
|
+
return this;
|
|
145
|
+
},
|
|
146
|
+
limit(amount) {
|
|
147
|
+
queryObj.limit = amount;
|
|
148
|
+
return this;
|
|
149
|
+
},
|
|
150
|
+
select(...fields) {
|
|
151
|
+
queryObj.select.push(...fields);
|
|
152
|
+
return this;
|
|
153
|
+
},
|
|
119
154
|
async find() {
|
|
120
|
-
|
|
155
|
+
const q = { path: queryObj.path };
|
|
156
|
+
if (queryObj.where.length)
|
|
157
|
+
q.where = JSON.stringify(queryObj.where);
|
|
158
|
+
if (queryObj.order.length)
|
|
159
|
+
q.order = JSON.stringify(queryObj.order);
|
|
160
|
+
if (queryObj.skip > 0)
|
|
161
|
+
q.skip = String(queryObj.skip);
|
|
162
|
+
if (queryObj.limit > 0)
|
|
163
|
+
q.limit = String(queryObj.limit);
|
|
164
|
+
if (queryObj.select.length)
|
|
165
|
+
q.select = queryObj.select.join(",");
|
|
166
|
+
return await fetchContent("query", q);
|
|
121
167
|
},
|
|
122
168
|
async findOne() {
|
|
123
169
|
const entries = await this.find();
|
|
124
170
|
return entries[0] || null;
|
|
125
171
|
},
|
|
172
|
+
async all() {
|
|
173
|
+
return await this.find();
|
|
174
|
+
},
|
|
175
|
+
async first() {
|
|
176
|
+
this.limit(1);
|
|
177
|
+
return await this.findOne();
|
|
178
|
+
},
|
|
179
|
+
async surround(targetPath) {
|
|
180
|
+
const entries = await this.find();
|
|
181
|
+
const index = entries.findIndex((e) => e.path === targetPath);
|
|
182
|
+
if (index === -1) {
|
|
183
|
+
return [null, null];
|
|
184
|
+
}
|
|
185
|
+
return [
|
|
186
|
+
index > 0 ? entries[index - 1] : null,
|
|
187
|
+
index < entries.length - 1 ? entries[index + 1] : null
|
|
188
|
+
];
|
|
189
|
+
},
|
|
126
190
|
async tree() {
|
|
127
|
-
|
|
191
|
+
const q = { path: queryObj.path };
|
|
192
|
+
return await fetchContent("tree", q);
|
|
128
193
|
}
|
|
129
194
|
};
|
|
195
|
+
return builder;
|
|
130
196
|
}
|
package/src/runtime/config.d.ts
CHANGED
|
@@ -71,6 +71,13 @@ export interface FlowModuleNitroConfig extends Record<string, unknown> {
|
|
|
71
71
|
routeRules: Record<string, Record<string, unknown>>;
|
|
72
72
|
runtimeConfig: Record<string, unknown>;
|
|
73
73
|
virtual: Record<string, string | (() => string | Promise<string>)>;
|
|
74
|
+
imports?: {
|
|
75
|
+
imports: Array<{
|
|
76
|
+
name: string;
|
|
77
|
+
from: string;
|
|
78
|
+
as?: string;
|
|
79
|
+
}>;
|
|
80
|
+
};
|
|
74
81
|
}
|
|
75
82
|
export interface FlowModuleViteConfig extends Record<string, unknown> {
|
|
76
83
|
plugins: unknown[];
|
package/src/runtime/modules.mjs
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function (nitroApp: any): void;
|
|
@@ -53,6 +53,26 @@ function replacePath(pattern, url) {
|
|
|
53
53
|
function combineName(name, dynamicName) {
|
|
54
54
|
return `${name.replace(/\/*$/, "")}/${dynamicName.replace(/^\/*/, "")}`;
|
|
55
55
|
}
|
|
56
|
+
function resolveUrlTarget(definitions, namePage, explicitDynamicName) {
|
|
57
|
+
if (explicitDynamicName || !namePage.includes("/")) {
|
|
58
|
+
return {
|
|
59
|
+
namePage,
|
|
60
|
+
dynamicName: explicitDynamicName
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const match = definitions.filter((candidate) => candidate.name && namePage.startsWith(`${candidate.name}/`)).sort((left, right) => right.name.length - left.name.length)[0];
|
|
64
|
+
if (!match) {
|
|
65
|
+
return {
|
|
66
|
+
namePage,
|
|
67
|
+
dynamicName: explicitDynamicName
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const dynamicName = namePage.slice(match.name.length + 1);
|
|
71
|
+
return {
|
|
72
|
+
namePage: match.name,
|
|
73
|
+
dynamicName: dynamicName || explicitDynamicName
|
|
74
|
+
};
|
|
75
|
+
}
|
|
56
76
|
function createContext(definitions, flowConfig, definition, locale, localePage, pathname, params, dynamic) {
|
|
57
77
|
return {
|
|
58
78
|
dynamic,
|
|
@@ -111,7 +131,8 @@ export async function loadPageDefinitions(projectRoot, flowConfig) {
|
|
|
111
131
|
return pages;
|
|
112
132
|
}
|
|
113
133
|
export async function getUrlFromDefinitions(definitions, flowConfig, namePage, localeCode = flowConfig.locale.locales[0], options = {}) {
|
|
114
|
-
const
|
|
134
|
+
const target = resolveUrlTarget(definitions, namePage, options.dynamicName);
|
|
135
|
+
const definition = definitions.find((candidate) => candidate.name === target.namePage);
|
|
115
136
|
if (!definition) {
|
|
116
137
|
return void 0;
|
|
117
138
|
}
|
|
@@ -126,9 +147,9 @@ export async function getUrlFromDefinitions(definitions, flowConfig, namePage, l
|
|
|
126
147
|
if (options.params) {
|
|
127
148
|
return replacePath(localizedRoute, options.params);
|
|
128
149
|
}
|
|
129
|
-
if (localePage.dynamic &&
|
|
150
|
+
if (localePage.dynamic && target.dynamicName) {
|
|
130
151
|
const entries = await getDynamicEntries(definitions, flowConfig, definition, localeCode, localePage);
|
|
131
|
-
const entry = entries.find((candidate) => candidate.name ===
|
|
152
|
+
const entry = entries.find((candidate) => candidate.name === target.dynamicName);
|
|
132
153
|
if (!entry) {
|
|
133
154
|
return void 0;
|
|
134
155
|
}
|