@monkeyplus/flow 6.0.13 → 6.0.15
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/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/renderer.mjs +4 -0
- package/src/main.d.ts +1 -1
- package/src/main.mjs +2 -1
- 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/public/vite.mjs +19 -6
- 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/virtual-pages.d.ts +1 -0
- package/src/runtime/virtual-pages.mjs +18 -0
|
@@ -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/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/main.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import 'virtual:flow/server-styles';
|
package/src/main.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { hydrateIslands } from "./runtime/islands.mjs";
|
|
2
1
|
import bundles from "virtual:flow/client-pages";
|
|
2
|
+
import { hydrateIslands } from "./runtime/islands.mjs";
|
|
3
|
+
import "virtual:flow/server-styles";
|
|
3
4
|
function readBoot() {
|
|
4
5
|
const element = document.getElementById("flow-boot");
|
|
5
6
|
if (!element?.textContent) {
|
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/public/vite.mjs
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
createVirtualLayoutContextsModule,
|
|
18
18
|
createVirtualLayoutsModule,
|
|
19
19
|
createVirtualPagesModule,
|
|
20
|
+
createVirtualServerStylesModule,
|
|
20
21
|
createVirtualTemplatesModule
|
|
21
22
|
} from "../runtime/virtual-pages.mjs";
|
|
22
23
|
const flowRestartPatterns = [
|
|
@@ -55,6 +56,9 @@ function getVirtualModuleIdsForPath(projectPath) {
|
|
|
55
56
|
if (projectPath === "flow.config.ts" || projectPath.startsWith("pages/")) {
|
|
56
57
|
ids.add("virtual:flow/pages");
|
|
57
58
|
}
|
|
59
|
+
if (projectPath.startsWith("views/")) {
|
|
60
|
+
ids.add("virtual:flow/server-styles");
|
|
61
|
+
}
|
|
58
62
|
if (projectPath.startsWith("views/templates/")) {
|
|
59
63
|
ids.add("virtual:flow/templates");
|
|
60
64
|
}
|
|
@@ -141,7 +145,7 @@ function createFlowHotReload(projectRoot, extraWatchPaths = []) {
|
|
|
141
145
|
});
|
|
142
146
|
}, 0);
|
|
143
147
|
}
|
|
144
|
-
async function handleServerChange(server, filePath, event) {
|
|
148
|
+
async function handleServerChange(server, filePath, event, hmrModules) {
|
|
145
149
|
const projectPath = toProjectPath(filePath);
|
|
146
150
|
invalidateFileModules(server, filePath, event);
|
|
147
151
|
invalidateVirtualModules(server, projectPath);
|
|
@@ -153,11 +157,21 @@ function createFlowHotReload(projectRoot, extraWatchPaths = []) {
|
|
|
153
157
|
if (!shouldReload) {
|
|
154
158
|
return false;
|
|
155
159
|
}
|
|
156
|
-
|
|
160
|
+
if (event === "change" && projectPath.endsWith(".vue") && hmrModules) {
|
|
161
|
+
const validModules = hmrModules.filter((m) => m && m.id);
|
|
162
|
+
const isStyleOnly = validModules.length > 0 && validModules.every((m) => m.id.includes("type=style"));
|
|
163
|
+
if (isStyleOnly) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
setTimeout(() => {
|
|
168
|
+
server.ws.send({ type: "full-reload", path: "*" });
|
|
169
|
+
}, 50);
|
|
157
170
|
return true;
|
|
158
171
|
}
|
|
159
172
|
return {
|
|
160
173
|
name: "flow:hot-reload",
|
|
174
|
+
enforce: "post",
|
|
161
175
|
configureServer(server) {
|
|
162
176
|
if (extraWatchPaths.length) {
|
|
163
177
|
server.watcher.add(extraWatchPaths);
|
|
@@ -170,9 +184,7 @@ function createFlowHotReload(projectRoot, extraWatchPaths = []) {
|
|
|
170
184
|
});
|
|
171
185
|
},
|
|
172
186
|
async handleHotUpdate(ctx) {
|
|
173
|
-
|
|
174
|
-
return [];
|
|
175
|
-
}
|
|
187
|
+
await handleServerChange(ctx.server, ctx.file, "change", ctx.modules);
|
|
176
188
|
}
|
|
177
189
|
};
|
|
178
190
|
}
|
|
@@ -184,7 +196,8 @@ function createFlowVirtualServerModules(projectRoot, flowConfig) {
|
|
|
184
196
|
["virtual:flow/templates", () => createVirtualTemplatesModule(projectRoot)],
|
|
185
197
|
["virtual:flow/layouts", () => createVirtualLayoutsModule(projectRoot)],
|
|
186
198
|
["virtual:flow/layout-contexts", () => createVirtualLayoutContextsModule(projectRoot)],
|
|
187
|
-
["virtual:flow/bases", () => createVirtualBaseTemplatesModule(projectRoot)]
|
|
199
|
+
["virtual:flow/bases", () => createVirtualBaseTemplatesModule(projectRoot)],
|
|
200
|
+
["virtual:flow/server-styles", () => createVirtualServerStylesModule(projectRoot)]
|
|
188
201
|
]);
|
|
189
202
|
return {
|
|
190
203
|
name: "flow:server-virtuals",
|
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;
|
|
@@ -7,3 +7,4 @@ export declare function createVirtualBaseTemplatesModule(projectRoot: string): s
|
|
|
7
7
|
export declare function createVirtualIslandsModule(projectRoot: string): string;
|
|
8
8
|
export declare function createVirtualClientPagesModule(projectRoot: string): string;
|
|
9
9
|
export declare function createVirtualClientPageAssetsModule(projectRoot: string): string;
|
|
10
|
+
export declare function createVirtualServerStylesModule(projectRoot: string): string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parse } from "vue/compiler-sfc";
|
|
1
2
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
3
|
import { basename, extname, resolve } from "node:path";
|
|
3
4
|
function collectFiles(rootDir, extensions, currentDir = rootDir) {
|
|
@@ -170,3 +171,20 @@ export function createVirtualClientPageAssetsModule(projectRoot) {
|
|
|
170
171
|
""
|
|
171
172
|
].join("\n");
|
|
172
173
|
}
|
|
174
|
+
export function createVirtualServerStylesModule(projectRoot) {
|
|
175
|
+
const viewsDir = resolve(projectRoot, "views");
|
|
176
|
+
const files = existsSync(viewsDir) ? collectFiles(viewsDir, [".vue"]) : [];
|
|
177
|
+
const imports = files.flatMap((filePath) => {
|
|
178
|
+
const code = readFileSync(filePath, "utf8");
|
|
179
|
+
const { descriptor } = parse(code);
|
|
180
|
+
return descriptor.styles.map((style, i) => {
|
|
181
|
+
return `import "${toAbsoluteImport(filePath)}?vue&type=style&index=${i}&lang.${style.lang || "css"}";`;
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
return [
|
|
185
|
+
...imports,
|
|
186
|
+
"",
|
|
187
|
+
"export default {};",
|
|
188
|
+
""
|
|
189
|
+
].join("\n");
|
|
190
|
+
}
|