@webround/editor 0.0.0-stage → 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/dist/blocks.d.ts +90 -0
- package/dist/blocks.js +119 -0
- package/dist/bridge.d.ts +96 -0
- package/dist/bridge.js +970 -0
- package/dist/check/aria.d.ts +19 -0
- package/dist/check/aria.js +106 -0
- package/dist/check/cli.d.ts +3 -0
- package/dist/check/cli.js +30 -0
- package/dist/check/css.d.ts +13 -0
- package/dist/check/css.js +63 -0
- package/dist/check/html.d.ts +8 -0
- package/dist/check/html.js +29 -0
- package/dist/check/index.d.ts +4 -0
- package/dist/check/index.js +5 -0
- package/dist/check/links.d.ts +7 -0
- package/dist/check/links.js +23 -0
- package/dist/check/orphans.d.ts +20 -0
- package/dist/check/orphans.js +57 -0
- package/dist/check/producer-code.d.ts +53 -0
- package/dist/check/producer-code.js +379 -0
- package/dist/check/producer-dom.d.ts +21 -0
- package/dist/check/producer-dom.js +52 -0
- package/dist/check/project.d.ts +20 -0
- package/dist/check/project.js +51 -0
- package/dist/check/rules.d.ts +7 -0
- package/dist/check/rules.js +517 -0
- package/dist/check/types.d.ts +160 -0
- package/dist/check/types.js +22 -0
- package/dist/components/WrAltcha.d.ts +5 -0
- package/dist/components/WrAltcha.js +8 -0
- package/dist/components/WrButton.d.ts +8 -0
- package/dist/components/WrButton.js +34 -0
- package/dist/components/WrForm.d.ts +6 -0
- package/dist/components/WrForm.js +29 -0
- package/dist/components/WrFormPrivacy.d.ts +4 -0
- package/dist/components/WrFormPrivacy.js +7 -0
- package/dist/components/WrFormSuccess.d.ts +4 -0
- package/dist/components/WrFormSuccess.js +12 -0
- package/dist/components/WrIframe.d.ts +12 -0
- package/dist/components/WrIframe.js +30 -0
- package/dist/components/WrImage.d.ts +9 -0
- package/dist/components/WrImage.js +28 -0
- package/dist/components/WrInput.d.ts +8 -0
- package/dist/components/WrInput.js +29 -0
- package/dist/components/WrLink.d.ts +10 -0
- package/dist/components/WrLink.js +38 -0
- package/dist/components/WrNav.d.ts +26 -0
- package/dist/components/WrNav.js +30 -0
- package/dist/components/WrSection.d.ts +11 -0
- package/dist/components/WrSection.js +5 -0
- package/dist/components/WrShared.d.ts +12 -0
- package/dist/components/WrShared.js +20 -0
- package/dist/components/WrText.d.ts +11 -0
- package/dist/components/WrText.js +41 -0
- package/dist/components/WrVideo.d.ts +9 -0
- package/dist/components/WrVideo.js +73 -0
- package/dist/components/text-editor/CodeThemeContext.d.ts +8 -0
- package/dist/components/text-editor/CodeThemeContext.js +10 -0
- package/dist/components/text-editor/ExampleTheme.d.ts +77 -0
- package/dist/components/text-editor/ExampleTheme.js +76 -0
- package/dist/components/text-editor/ToolbarContext.d.ts +17 -0
- package/dist/components/text-editor/ToolbarContext.js +32 -0
- package/dist/components/text-editor/WrTextEditor.d.ts +13 -0
- package/dist/components/text-editor/WrTextEditor.js +48 -0
- package/dist/components/text-editor/common/constants.d.ts +109 -0
- package/dist/components/text-editor/common/constants.js +58 -0
- package/dist/components/text-editor/common/surface.d.ts +3 -0
- package/dist/components/text-editor/common/surface.js +8 -0
- package/dist/components/text-editor/common/utils.d.ts +7 -0
- package/dist/components/text-editor/common/utils.js +78 -0
- package/dist/components/text-editor/hooks/useLexicalCommitOnBlur.d.ts +5 -0
- package/dist/components/text-editor/hooks/useLexicalCommitOnBlur.js +41 -0
- package/dist/components/text-editor/hooks/useLexicalMarkdown.d.ts +33 -0
- package/dist/components/text-editor/hooks/useLexicalMarkdown.js +284 -0
- package/dist/components/text-editor/nodes/CodeBlockNode.d.ts +23 -0
- package/dist/components/text-editor/nodes/CodeBlockNode.js +138 -0
- package/dist/components/text-editor/nodes/HorizontalRuleNode.d.ts +13 -0
- package/dist/components/text-editor/nodes/HorizontalRuleNode.js +22 -0
- package/dist/components/text-editor/plugins/CodeBlockPlugin.d.ts +2 -0
- package/dist/components/text-editor/plugins/CodeBlockPlugin.js +38 -0
- package/dist/components/text-editor/plugins/FontPlugin.d.ts +12 -0
- package/dist/components/text-editor/plugins/FontPlugin.js +114 -0
- package/dist/components/text-editor/plugins/FontSizePlugin.d.ts +7 -0
- package/dist/components/text-editor/plugins/FontSizePlugin.js +105 -0
- package/dist/components/text-editor/plugins/FontWeightPlugin.d.ts +6 -0
- package/dist/components/text-editor/plugins/FontWeightPlugin.js +103 -0
- package/dist/components/text-editor/plugins/HTMLTagPlugin.d.ts +7 -0
- package/dist/components/text-editor/plugins/HTMLTagPlugin.js +82 -0
- package/dist/components/text-editor/plugins/HorizontalRulePlugin.d.ts +2 -0
- package/dist/components/text-editor/plugins/HorizontalRulePlugin.js +35 -0
- package/dist/components/text-editor/plugins/LineSpacingPlugin.d.ts +7 -0
- package/dist/components/text-editor/plugins/LineSpacingPlugin.js +83 -0
- package/dist/components/text-editor/plugins/LinkPlugin.d.ts +8 -0
- package/dist/components/text-editor/plugins/LinkPlugin.js +183 -0
- package/dist/components/text-editor/plugins/ListToolbarPlugin.d.ts +7 -0
- package/dist/components/text-editor/plugins/ListToolbarPlugin.js +40 -0
- package/dist/components/text-editor/plugins/LoadContentPlugin.d.ts +4 -0
- package/dist/components/text-editor/plugins/LoadContentPlugin.js +63 -0
- package/dist/components/text-editor/plugins/MarkdownExportPlugin.d.ts +4 -0
- package/dist/components/text-editor/plugins/MarkdownExportPlugin.js +17 -0
- package/dist/components/text-editor/plugins/MarkdownPastePlugin.d.ts +2 -0
- package/dist/components/text-editor/plugins/MarkdownPastePlugin.js +45 -0
- package/dist/components/text-editor/plugins/SanitizePastePlugin.d.ts +3 -0
- package/dist/components/text-editor/plugins/SanitizePastePlugin.js +40 -0
- package/dist/components/text-editor/plugins/TextAlignPlugin.d.ts +7 -0
- package/dist/components/text-editor/plugins/TextAlignPlugin.js +65 -0
- package/dist/components/text-editor/plugins/TextColorPlugin.d.ts +7 -0
- package/dist/components/text-editor/plugins/TextColorPlugin.js +117 -0
- package/dist/components/text-editor/plugins/ToolbarPlugin.d.ts +10 -0
- package/dist/components/text-editor/plugins/ToolbarPlugin.js +73 -0
- package/dist/components/text-editor/theme-colors.d.ts +11 -0
- package/dist/components/text-editor/theme-colors.js +28 -0
- package/dist/context/WrAppContext.d.ts +35 -0
- package/dist/context/WrAppContext.js +113 -0
- package/dist/context/WrLayout.d.ts +15 -0
- package/dist/context/WrLayout.js +22 -0
- package/dist/context/WrPageContext.d.ts +28 -0
- package/dist/context/WrPageContext.js +282 -0
- package/dist/context/restoreValues.d.ts +2 -0
- package/dist/context/restoreValues.js +6 -0
- package/dist/contrast.d.ts +45 -0
- package/dist/contrast.js +122 -0
- package/dist/drop.d.ts +7 -0
- package/dist/drop.js +51 -0
- package/dist/gaps.d.ts +14 -0
- package/dist/gaps.js +50 -0
- package/dist/hooks/useWrMessage.d.ts +5 -0
- package/dist/hooks/useWrMessage.js +24 -0
- package/dist/hooks/useWrSave.d.ts +2 -0
- package/dist/hooks/useWrSave.js +11 -0
- package/dist/index.css +467 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +20 -0
- package/dist/interactive.d.ts +8 -0
- package/dist/interactive.js +13 -0
- package/dist/measure-rules.d.ts +34 -0
- package/dist/measure-rules.js +54 -0
- package/dist/measure.d.ts +11 -0
- package/dist/measure.js +139 -0
- package/dist/messages.d.ts +860 -0
- package/dist/messages.js +2 -0
- package/dist/nav.d.ts +5 -0
- package/dist/nav.js +10 -0
- package/dist/operations/animations-store.d.ts +6 -0
- package/dist/operations/animations-store.js +63 -0
- package/dist/operations/cli.d.ts +3 -0
- package/dist/operations/cli.js +52 -0
- package/dist/operations/comments-store.d.ts +17 -0
- package/dist/operations/comments-store.js +62 -0
- package/dist/operations/context.d.ts +12 -0
- package/dist/operations/context.js +35 -0
- package/dist/operations/define.d.ts +81 -0
- package/dist/operations/define.js +86 -0
- package/dist/operations/index.d.ts +6 -0
- package/dist/operations/index.js +6 -0
- package/dist/operations/ops/check.d.ts +5 -0
- package/dist/operations/ops/check.js +53 -0
- package/dist/operations/ops/comments.d.ts +6 -0
- package/dist/operations/ops/comments.js +133 -0
- package/dist/operations/ops/content.d.ts +16 -0
- package/dist/operations/ops/content.js +73 -0
- package/dist/operations/ops/json.d.ts +4 -0
- package/dist/operations/ops/json.js +27 -0
- package/dist/operations/ops/langs.d.ts +4 -0
- package/dist/operations/ops/langs.js +32 -0
- package/dist/operations/ops/nodes.d.ts +8 -0
- package/dist/operations/ops/nodes.js +270 -0
- package/dist/operations/ops/pages.d.ts +8 -0
- package/dist/operations/ops/pages.js +154 -0
- package/dist/operations/ops/project.d.ts +36 -0
- package/dist/operations/ops/project.js +97 -0
- package/dist/operations/ops/seo.d.ts +17 -0
- package/dist/operations/ops/seo.js +358 -0
- package/dist/operations/ops/style.d.ts +76 -0
- package/dist/operations/ops/style.js +268 -0
- package/dist/operations/ops/stylesets.d.ts +9 -0
- package/dist/operations/ops/stylesets.js +145 -0
- package/dist/operations/ops/theme.d.ts +6 -0
- package/dist/operations/ops/theme.js +304 -0
- package/dist/operations/registry.d.ts +8 -0
- package/dist/operations/registry.js +19 -0
- package/dist/operations/stylesets-store.d.ts +50 -0
- package/dist/operations/stylesets-store.js +216 -0
- package/dist/registry.d.ts +5 -0
- package/dist/registry.js +24 -0
- package/dist/save.d.ts +65 -0
- package/dist/save.js +86 -0
- package/dist/schema.d.ts +69 -0
- package/dist/schema.js +110 -0
- package/dist/scope.d.ts +41 -0
- package/dist/scope.js +44 -0
- package/dist/vite-plugins/ast.d.ts +3 -0
- package/dist/vite-plugins/ast.js +238 -0
- package/dist/vite-plugins/core-swap.d.ts +3 -0
- package/dist/vite-plugins/core-swap.js +57 -0
- package/dist/vite-plugins/create-page.d.ts +4 -0
- package/dist/vite-plugins/create-page.js +59 -0
- package/dist/vite-plugins/guard.d.ts +15 -0
- package/dist/vite-plugins/guard.js +35 -0
- package/dist/vite-plugins/head.d.ts +13 -0
- package/dist/vite-plugins/head.js +53 -0
- package/dist/vite-plugins/index.d.ts +7 -0
- package/dist/vite-plugins/index.js +7 -0
- package/dist/vite-plugins/jsx-tree.d.ts +118 -0
- package/dist/vite-plugins/jsx-tree.js +686 -0
- package/dist/vite-plugins/langs.d.ts +3 -0
- package/dist/vite-plugins/langs.js +44 -0
- package/dist/vite-plugins/operations.d.ts +10 -0
- package/dist/vite-plugins/operations.js +66 -0
- package/dist/vite-plugins/pages-api.d.ts +3 -0
- package/dist/vite-plugins/pages-api.js +112 -0
- package/dist/vite-plugins/pages.d.ts +3 -0
- package/dist/vite-plugins/pages.js +64 -0
- package/dist/vite-plugins/read.d.ts +3 -0
- package/dist/vite-plugins/read.js +45 -0
- package/dist/vite-plugins/seo-files.d.ts +8 -0
- package/dist/vite-plugins/seo-files.js +42 -0
- package/dist/vite-plugins/theme.d.ts +3 -0
- package/dist/vite-plugins/theme.js +207 -0
- package/dist/vite-plugins/update.d.ts +3 -0
- package/dist/vite-plugins/update.js +46 -0
- package/dist/vite-plugins/wr-plugins.d.ts +15 -0
- package/dist/vite-plugins/wr-plugins.js +34 -0
- package/dist/vite-plugins.d.ts +11 -0
- package/dist/vite-plugins.js +518 -0
- package/package.json +96 -5
- package/README.md +0 -3
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { wrPageConfigPath, wrPageFilePath } from '@webround/core/routing';
|
|
4
|
+
import { defineOperation, flag, invalid, OperationError, text } from '../define.js';
|
|
5
|
+
import { isSafeComponentName, isSafePageName } from '../context.js';
|
|
6
|
+
import { dropScopeComments, renameScopeComments } from '../comments-store.js';
|
|
7
|
+
import { ROUTES_FILE, readRoutes } from './project.js';
|
|
8
|
+
/** The file a new page starts as. */
|
|
9
|
+
export function wrPageScaffold(component) {
|
|
10
|
+
return `import { WrSection } from '@webround/core';
|
|
11
|
+
|
|
12
|
+
export default function ${component}() {
|
|
13
|
+
return <WrSection id="root" />;
|
|
14
|
+
}
|
|
15
|
+
`;
|
|
16
|
+
}
|
|
17
|
+
const writeRoutes = (srcRoot, routes) => fs.writeFileSync(path.join(srcRoot, ROUTES_FILE), JSON.stringify(routes, null, 2));
|
|
18
|
+
export const pageList = defineOperation({
|
|
19
|
+
name: 'page-list',
|
|
20
|
+
readOnly: true,
|
|
21
|
+
summary: 'The pages of the site: name, component, and whether the shared header and footer are shown on each.',
|
|
22
|
+
run: (_input, ctx) => ({
|
|
23
|
+
pages: Object.entries(readRoutes(ctx)).map(([pageName, entry]) => ({
|
|
24
|
+
pageName,
|
|
25
|
+
component: entry.component,
|
|
26
|
+
lazy: entry.lazy,
|
|
27
|
+
// Shown unless the page says otherwise (see core/routing)
|
|
28
|
+
header: entry.header !== false,
|
|
29
|
+
footer: entry.footer !== false,
|
|
30
|
+
})),
|
|
31
|
+
}),
|
|
32
|
+
});
|
|
33
|
+
export const pageCreate = defineOperation({
|
|
34
|
+
name: 'page-create',
|
|
35
|
+
summary: 'Creates a page: its component, its (empty) JSON of values and its entry in the routes.',
|
|
36
|
+
params: [
|
|
37
|
+
{ name: 'pageName', type: 'string', positional: true, required: true, help: 'the page name: it is the URL (letters, digits, - and _)' },
|
|
38
|
+
{ name: 'component', type: 'string', required: true, help: 'the name of its component, e.g. About (a file in src/pages/)' },
|
|
39
|
+
{ name: 'lazy', type: 'boolean', default: false, help: 'load the page only when it is visited' },
|
|
40
|
+
],
|
|
41
|
+
run: (input, ctx) => {
|
|
42
|
+
const pageName = text(input, 'pageName');
|
|
43
|
+
const component = text(input, 'component');
|
|
44
|
+
if (!isSafePageName(pageName) || !isSafeComponentName(component))
|
|
45
|
+
throw invalid('invalid pageName or component');
|
|
46
|
+
const lazy = flag(input, 'lazy');
|
|
47
|
+
const pagePath = path.join(ctx.srcRoot, wrPageConfigPath(pageName));
|
|
48
|
+
const componentPath = path.join(ctx.srcRoot, wrPageFilePath(component));
|
|
49
|
+
if (fs.existsSync(pagePath) || fs.existsSync(componentPath))
|
|
50
|
+
throw new OperationError(409, 'page-exists', 'page or component already exists');
|
|
51
|
+
fs.mkdirSync(path.dirname(pagePath), { recursive: true });
|
|
52
|
+
fs.writeFileSync(pagePath, JSON.stringify({}, null, 2));
|
|
53
|
+
fs.mkdirSync(path.dirname(componentPath), { recursive: true });
|
|
54
|
+
fs.writeFileSync(componentPath, wrPageScaffold(component));
|
|
55
|
+
const routes = readRoutes(ctx);
|
|
56
|
+
routes[pageName] = { component, ...(lazy ? { lazy: true } : {}) };
|
|
57
|
+
writeRoutes(ctx.srcRoot, routes);
|
|
58
|
+
return { pageName };
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
export const pageDelete = defineOperation({
|
|
62
|
+
name: 'page-delete',
|
|
63
|
+
destructive: true,
|
|
64
|
+
summary: 'Deletes a page: its component, its JSON of values, its comments and its entry in the routes.',
|
|
65
|
+
params: [{ name: 'pageName', type: 'string', positional: true, required: true, help: 'the page to delete' }],
|
|
66
|
+
run: (input, ctx) => {
|
|
67
|
+
const pageName = text(input, 'pageName');
|
|
68
|
+
if (!isSafePageName(pageName))
|
|
69
|
+
throw invalid('invalid pageName');
|
|
70
|
+
const routes = readRoutes(ctx);
|
|
71
|
+
const entry = routes[pageName];
|
|
72
|
+
if (!entry)
|
|
73
|
+
throw new OperationError(404, 'page-not-found', 'page not found');
|
|
74
|
+
if (!isSafeComponentName(entry.component))
|
|
75
|
+
throw new OperationError(500, 'invalid-routes', 'invalid component in routes.json');
|
|
76
|
+
const pagePath = path.join(ctx.srcRoot, wrPageConfigPath(pageName));
|
|
77
|
+
const componentPath = path.join(ctx.srcRoot, wrPageFilePath(entry.component));
|
|
78
|
+
if (fs.existsSync(pagePath))
|
|
79
|
+
fs.unlinkSync(pagePath);
|
|
80
|
+
if (fs.existsSync(componentPath))
|
|
81
|
+
fs.unlinkSync(componentPath);
|
|
82
|
+
delete routes[pageName];
|
|
83
|
+
writeRoutes(ctx.srcRoot, routes);
|
|
84
|
+
dropScopeComments(ctx, pageName);
|
|
85
|
+
return { pageName };
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
export const pageUpdate = defineOperation({
|
|
89
|
+
name: 'page-update',
|
|
90
|
+
summary: 'Changes a page: renames it or its component, loads it lazily or not, shows or hides the shared header and footer on it.',
|
|
91
|
+
params: [
|
|
92
|
+
{ name: 'pageName', type: 'string', positional: true, required: true, help: 'the page' },
|
|
93
|
+
{ name: 'newPageName', type: 'string', help: 'a new name (its JSON of values moves with it)' },
|
|
94
|
+
{ name: 'newComponent', type: 'string', help: 'a new component name (its file moves with it)' },
|
|
95
|
+
{ name: 'lazy', type: 'boolean', help: 'load the page only when it is visited' },
|
|
96
|
+
{ name: 'header', type: 'boolean', help: 'show the shared header on this page' },
|
|
97
|
+
{ name: 'footer', type: 'boolean', help: 'show the shared footer on this page' },
|
|
98
|
+
],
|
|
99
|
+
run: (input, ctx) => {
|
|
100
|
+
const pageName = text(input, 'pageName');
|
|
101
|
+
if (!isSafePageName(pageName))
|
|
102
|
+
throw invalid('invalid pageName');
|
|
103
|
+
const routes = readRoutes(ctx);
|
|
104
|
+
const entry = routes[pageName];
|
|
105
|
+
if (!entry)
|
|
106
|
+
throw new OperationError(404, 'page-not-found', 'page not found');
|
|
107
|
+
if (!isSafeComponentName(entry.component))
|
|
108
|
+
throw new OperationError(500, 'invalid-routes', 'invalid component in routes.json');
|
|
109
|
+
const finalPageName = text(input, 'newPageName') ?? pageName;
|
|
110
|
+
const finalComponent = text(input, 'newComponent') ?? entry.component;
|
|
111
|
+
if (!isSafePageName(finalPageName) || !isSafeComponentName(finalComponent))
|
|
112
|
+
throw invalid('invalid newPageName or newComponent');
|
|
113
|
+
if (finalPageName !== pageName && routes[finalPageName])
|
|
114
|
+
throw new OperationError(409, 'page-exists', 'target page name already exists');
|
|
115
|
+
const pagePath = path.join(ctx.srcRoot, wrPageConfigPath(pageName));
|
|
116
|
+
const componentPath = path.join(ctx.srcRoot, wrPageFilePath(entry.component));
|
|
117
|
+
const newPagePath = path.join(ctx.srcRoot, wrPageConfigPath(finalPageName));
|
|
118
|
+
const newComponentPath = path.join(ctx.srcRoot, wrPageFilePath(finalComponent));
|
|
119
|
+
if (fs.existsSync(pagePath) && pagePath !== newPagePath) {
|
|
120
|
+
fs.mkdirSync(path.dirname(newPagePath), { recursive: true });
|
|
121
|
+
fs.renameSync(pagePath, newPagePath);
|
|
122
|
+
}
|
|
123
|
+
if (fs.existsSync(componentPath) && componentPath !== newComponentPath) {
|
|
124
|
+
fs.mkdirSync(path.dirname(newComponentPath), { recursive: true });
|
|
125
|
+
fs.renameSync(componentPath, newComponentPath);
|
|
126
|
+
}
|
|
127
|
+
delete routes[pageName];
|
|
128
|
+
// The entry is kept (header/footer and whatever comes next): only what the request says changes. `true` is the default of
|
|
129
|
+
// header and footer and is not written.
|
|
130
|
+
const next = { ...entry, component: finalComponent };
|
|
131
|
+
const lazy = flag(input, 'lazy');
|
|
132
|
+
if (lazy !== undefined) {
|
|
133
|
+
if (lazy)
|
|
134
|
+
next.lazy = true;
|
|
135
|
+
else
|
|
136
|
+
delete next.lazy;
|
|
137
|
+
}
|
|
138
|
+
for (const key of ['header', 'footer']) {
|
|
139
|
+
const value = flag(input, key);
|
|
140
|
+
if (value === undefined)
|
|
141
|
+
continue;
|
|
142
|
+
if (value === false)
|
|
143
|
+
next[key] = false;
|
|
144
|
+
else
|
|
145
|
+
delete next[key];
|
|
146
|
+
}
|
|
147
|
+
routes[finalPageName] = next;
|
|
148
|
+
writeRoutes(ctx.srcRoot, routes);
|
|
149
|
+
renameScopeComments(ctx, pageName, finalPageName);
|
|
150
|
+
return { pageName: finalPageName };
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
export const PAGE_OPERATIONS = [pageList, pageCreate, pageDelete, pageUpdate];
|
|
154
|
+
//# sourceMappingURL=pages.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type WrLangConfig, type WrRoutesFile, type WrSharedScope } from '@webround/core/routing';
|
|
2
|
+
import type { WrSeoSite } from '@webround/core/seo';
|
|
3
|
+
import { type OperationContext } from '../define.js';
|
|
4
|
+
/** A page, or a shared component (the header, the footer, a card): the file of its code and the JSON of its values (both relative to `src/`). */
|
|
5
|
+
export interface ScopeFiles {
|
|
6
|
+
name: string;
|
|
7
|
+
file: string;
|
|
8
|
+
json: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function readJsonFile<T = unknown>(ctx: OperationContext, relative: string): T | null;
|
|
11
|
+
/** Merges `patch` into a JSON file (created if missing): a `null` value removes the key. */
|
|
12
|
+
export declare function patchJsonFile(ctx: OperationContext, relative: string, patch: Record<string, unknown>): void;
|
|
13
|
+
export declare const ROUTES_FILE = "config/routes.json";
|
|
14
|
+
export declare const LANGS_FILE = "config/langs.json";
|
|
15
|
+
export declare const SEO_FILE = "config/seo.json";
|
|
16
|
+
export declare function readRoutes(ctx: OperationContext): WrRoutesFile;
|
|
17
|
+
/** Every shared scope of the project: its header and footer, and the shared components it declares in `config/shared.json`. THE list. */
|
|
18
|
+
export declare const sharedScopesOf: (ctx: OperationContext) => WrSharedScope[];
|
|
19
|
+
export declare const readSeoSite: (ctx: OperationContext) => WrSeoSite;
|
|
20
|
+
/**
|
|
21
|
+
* The key a language has in the JSON: `default` for the site's default language (also when it is asked for by its code), the code for the
|
|
22
|
+
* others. THE way an operation turns a `lang` parameter into where a translation is written: a language the site does not have is refused
|
|
23
|
+
* (a typo must not create a stray key in the JSON).
|
|
24
|
+
*/
|
|
25
|
+
export declare function langKeyOf(ctx: OperationContext, requested: string | undefined): string;
|
|
26
|
+
export declare const readLangs: (ctx: OperationContext) => WrLangConfig | null;
|
|
27
|
+
/** The files of a page by its name (`home`), or of a shared component by its (`_shared/header`, `_shared/product-card`). */
|
|
28
|
+
export declare function scopeFilesOf(ctx: OperationContext, page: string): ScopeFiles;
|
|
29
|
+
/** Every scope a visitor sees on a page: its header, the page and its footer, in the order they are on the page. */
|
|
30
|
+
export declare function scopesOfPage(ctx: OperationContext, page: string): ScopeFiles[];
|
|
31
|
+
/**
|
|
32
|
+
* Which scope an operation means: by `page` (a name) for a caller that knows the site by its pages, or by `file` (the code file, relative
|
|
33
|
+
* to `src/`) for the editor's own page, which already knows it. Exactly one of the two.
|
|
34
|
+
*/
|
|
35
|
+
export declare function scopeOfInput(ctx: OperationContext, input: Record<string, unknown>): ScopeFiles;
|
|
36
|
+
//# sourceMappingURL=project.d.ts.map
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { WR_SHARED_FILE, WR_SHARED_SCOPES, wrLangKey, wrPageConfigPath, wrPageFilePath, wrSharedScopes } from '@webround/core/routing';
|
|
4
|
+
import { OperationError } from '../define.js';
|
|
5
|
+
import { isSafePageName } from '../context.js';
|
|
6
|
+
export function readJsonFile(ctx, relative) {
|
|
7
|
+
const file = ctx.resolveInSrc(relative);
|
|
8
|
+
if (!file || !fs.existsSync(file))
|
|
9
|
+
return null;
|
|
10
|
+
return JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
11
|
+
}
|
|
12
|
+
/** Merges `patch` into a JSON file (created if missing): a `null` value removes the key. */
|
|
13
|
+
export function patchJsonFile(ctx, relative, patch) {
|
|
14
|
+
const file = ctx.resolveInSrc(relative);
|
|
15
|
+
if (!file)
|
|
16
|
+
throw new OperationError(400, 'invalid-file', 'invalid file path');
|
|
17
|
+
const current = (fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf-8')) : {});
|
|
18
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
19
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
20
|
+
if (value === null)
|
|
21
|
+
delete current[key];
|
|
22
|
+
else
|
|
23
|
+
current[key] = value;
|
|
24
|
+
}
|
|
25
|
+
fs.writeFileSync(file, JSON.stringify(current, null, 2));
|
|
26
|
+
}
|
|
27
|
+
export const ROUTES_FILE = 'config/routes.json';
|
|
28
|
+
export const LANGS_FILE = 'config/langs.json';
|
|
29
|
+
export const SEO_FILE = 'config/seo.json';
|
|
30
|
+
export function readRoutes(ctx) {
|
|
31
|
+
return readJsonFile(ctx, ROUTES_FILE) ?? {};
|
|
32
|
+
}
|
|
33
|
+
/** Every shared scope of the project: its header and footer, and the shared components it declares in `config/shared.json`. THE list. */
|
|
34
|
+
export const sharedScopesOf = (ctx) => wrSharedScopes(readJsonFile(ctx, WR_SHARED_FILE));
|
|
35
|
+
export const readSeoSite = (ctx) => readJsonFile(ctx, SEO_FILE) ?? {};
|
|
36
|
+
/**
|
|
37
|
+
* The key a language has in the JSON: `default` for the site's default language (also when it is asked for by its code), the code for the
|
|
38
|
+
* others. THE way an operation turns a `lang` parameter into where a translation is written: a language the site does not have is refused
|
|
39
|
+
* (a typo must not create a stray key in the JSON).
|
|
40
|
+
*/
|
|
41
|
+
export function langKeyOf(ctx, requested) {
|
|
42
|
+
const langs = readLangs(ctx);
|
|
43
|
+
const lang = requested ?? 'default';
|
|
44
|
+
if (lang === 'default')
|
|
45
|
+
return 'default';
|
|
46
|
+
if (!langs)
|
|
47
|
+
throw new OperationError(400, 'invalid-language', 'the site is not multilingual: there is only the default language');
|
|
48
|
+
if (!langs.available.includes(lang))
|
|
49
|
+
throw new OperationError(400, 'invalid-language', `"${lang}" is not a language of the site (${langs.available.join(', ')})`);
|
|
50
|
+
return wrLangKey(lang, langs);
|
|
51
|
+
}
|
|
52
|
+
export const readLangs = (ctx) => readJsonFile(ctx, LANGS_FILE);
|
|
53
|
+
/** The files of a page by its name (`home`), or of a shared component by its (`_shared/header`, `_shared/product-card`). */
|
|
54
|
+
export function scopeFilesOf(ctx, page) {
|
|
55
|
+
const shared = sharedScopesOf(ctx).find(scope => scope.name === page);
|
|
56
|
+
if (shared)
|
|
57
|
+
return { name: shared.name, file: shared.file, json: wrPageConfigPath(shared.name) };
|
|
58
|
+
if (!isSafePageName(page))
|
|
59
|
+
throw new OperationError(400, 'invalid-page', `invalid page name "${page}"`);
|
|
60
|
+
const entry = readRoutes(ctx)[page];
|
|
61
|
+
if (!entry)
|
|
62
|
+
throw new OperationError(404, 'page-not-found', `no page called "${page}"`);
|
|
63
|
+
return { name: page, file: wrPageFilePath(entry.component), json: wrPageConfigPath(page) };
|
|
64
|
+
}
|
|
65
|
+
/** Every scope a visitor sees on a page: its header, the page and its footer, in the order they are on the page. */
|
|
66
|
+
export function scopesOfPage(ctx, page) {
|
|
67
|
+
const entry = readRoutes(ctx)[page];
|
|
68
|
+
if (!entry)
|
|
69
|
+
throw new OperationError(404, 'page-not-found', `no page called "${page}"`);
|
|
70
|
+
return [
|
|
71
|
+
...(entry.header !== false ? [scopeFilesOf(ctx, WR_SHARED_SCOPES.header.name)] : []),
|
|
72
|
+
scopeFilesOf(ctx, page),
|
|
73
|
+
...(entry.footer !== false ? [scopeFilesOf(ctx, WR_SHARED_SCOPES.footer.name)] : []),
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Which scope an operation means: by `page` (a name) for a caller that knows the site by its pages, or by `file` (the code file, relative
|
|
78
|
+
* to `src/`) for the editor's own page, which already knows it. Exactly one of the two.
|
|
79
|
+
*/
|
|
80
|
+
export function scopeOfInput(ctx, input) {
|
|
81
|
+
const { page, file } = input;
|
|
82
|
+
if (typeof page === 'string' && page)
|
|
83
|
+
return scopeFilesOf(ctx, page);
|
|
84
|
+
if (typeof file === 'string' && file) {
|
|
85
|
+
if (!ctx.resolveInSrc(file))
|
|
86
|
+
throw new OperationError(400, 'invalid-file', 'invalid file');
|
|
87
|
+
// The JSON of a file: the page that uses it, or the shared scope it belongs to
|
|
88
|
+
const shared = sharedScopesOf(ctx).find(scope => scope.file === file);
|
|
89
|
+
if (shared)
|
|
90
|
+
return { name: shared.name, file, json: wrPageConfigPath(shared.name) };
|
|
91
|
+
const routes = readRoutes(ctx);
|
|
92
|
+
const name = Object.keys(routes).find(key => wrPageFilePath(routes[key].component) === file);
|
|
93
|
+
return { name: name ?? file, file, json: wrPageConfigPath(name ?? file) };
|
|
94
|
+
}
|
|
95
|
+
throw new OperationError(400, 'invalid-input', 'page (or file) required');
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=project.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type OperationContext } from '../define.js';
|
|
2
|
+
/**
|
|
3
|
+
* Writes robots.txt and the sitemaps into `public/` (only what changed). Nothing is written while the site does not know its public address.
|
|
4
|
+
* ONE function: the dev server's watcher calls it whenever pages, languages or SEO data change, `seo-sync` calls it on demand.
|
|
5
|
+
*/
|
|
6
|
+
export declare function writeSeoFiles(ctx: OperationContext): {
|
|
7
|
+
written: string[];
|
|
8
|
+
reason?: 'no-site-url';
|
|
9
|
+
};
|
|
10
|
+
export declare const seoGet: import("../define.js").Operation;
|
|
11
|
+
export declare const seoSuggest: import("../define.js").Operation;
|
|
12
|
+
export declare const seoSet: import("../define.js").Operation;
|
|
13
|
+
export declare const seoSiteSet: import("../define.js").Operation;
|
|
14
|
+
export declare const seoSync: import("../define.js").Operation;
|
|
15
|
+
export declare const seoSnapshotTargets: import("../define.js").Operation;
|
|
16
|
+
export declare const SEO_OPERATIONS: import("../define.js").Operation[];
|
|
17
|
+
//# sourceMappingURL=seo.d.ts.map
|