@blinkk/root 1.0.0-alpha.8 → 1.0.0-beta.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/bin/root.js +1 -1
- package/dist/chunk-DTEQ2AIW.js +31 -0
- package/dist/chunk-DTEQ2AIW.js.map +1 -0
- package/dist/chunk-GGQGZ7ZE.js +61 -0
- package/dist/chunk-GGQGZ7ZE.js.map +1 -0
- package/dist/chunk-WTSNW7BB.js +68 -0
- package/dist/chunk-WTSNW7BB.js.map +1 -0
- package/dist/cli.js +777 -439
- package/dist/cli.js.map +1 -1
- package/dist/config-872b068d.d.ts +343 -0
- package/dist/core.d.ts +122 -19
- package/dist/core.js +78 -10
- package/dist/core.js.map +1 -1
- package/dist/render.d.ts +5 -61
- package/dist/render.js +417 -197
- package/dist/render.js.map +1 -1
- package/package.json +23 -25
- package/dist/chunk-ZV52A6YZ.js +0 -113
- package/dist/chunk-ZV52A6YZ.js.map +0 -1
- package/dist/types-2af24c42.d.ts +0 -66
package/bin/root.js
CHANGED
|
@@ -20,7 +20,7 @@ async function main() {
|
|
|
20
20
|
.command('build [path]')
|
|
21
21
|
.description('generates a static build')
|
|
22
22
|
.option('--ssr-only', 'produce a ssr-only build')
|
|
23
|
-
.option('--mode', 'see: https://vitejs.dev/guide/env-and-mode.html#modes', 'production')
|
|
23
|
+
.option('--mode <mode>', 'see: https://vitejs.dev/guide/env-and-mode.html#modes', 'production')
|
|
24
24
|
.action(build);
|
|
25
25
|
program
|
|
26
26
|
.command('dev [path]')
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/core/plugin.ts
|
|
2
|
+
async function configureServerPlugins(server, callback, plugins, options) {
|
|
3
|
+
const postHooks = [];
|
|
4
|
+
for (const plugin of plugins) {
|
|
5
|
+
if (plugin.configureServer) {
|
|
6
|
+
const postHook = await plugin.configureServer(server, options);
|
|
7
|
+
if (postHook) {
|
|
8
|
+
postHooks.push(postHook);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
callback();
|
|
13
|
+
for (const postHook of postHooks) {
|
|
14
|
+
await postHook();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function getVitePlugins(plugins) {
|
|
18
|
+
const vitePlugins = [];
|
|
19
|
+
for (const plugin of plugins) {
|
|
20
|
+
if (plugin.vitePlugins) {
|
|
21
|
+
vitePlugins.push(...plugin.vitePlugins);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return vitePlugins;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
configureServerPlugins,
|
|
29
|
+
getVitePlugins
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=chunk-DTEQ2AIW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/plugin.ts"],"sourcesContent":["import {PluginOption as VitePlugin} from 'vite';\nimport {RootConfig} from './config';\nimport {Server} from './types';\n\ntype MaybePromise<T> = T | Promise<T>;\n\nexport type ConfigureServerHook = (\n server: Server,\n options: ConfigureServerOptions\n) => MaybePromise<void> | MaybePromise<() => void>;\n\nexport interface ConfigureServerOptions {\n type: 'dev' | 'preview' | 'prod';\n rootConfig: RootConfig;\n}\n\nexport interface Plugin {\n [key: string]: any;\n /** The name of the plugin. */\n name?: string;\n /**\n * Configures the root.js express server. Any middleware defined by the plugin\n * will be added to the server first. If a callback fn is returned, it will\n * be called after the root.js middlewares are added.\n */\n configureServer?: ConfigureServerHook;\n /**\n * Returns a list of deps to bundle for ssr. The files will be bundled and\n * output to `dist/server/`. The return value should be a map of\n * `{output filename => input filepath}`.\n *\n * E.g. a value of `{foo: 'path/to/bar.js'}` will output `dist/server/foo.js`.\n *\n * @experimental This config is subject to change to be incorporated into a\n * broader config option called \"ssr\" or \"ssrOptions\".\n */\n ssrInput?: () => {[entryAlias: string]: string};\n /** Adds vite plugins. */\n vitePlugins?: VitePlugin[];\n}\n\n/**\n * Runs the pre-hook configureServer method of every plugin, calls a callback\n * function, and then runs the configureServer's post-hook if provided. Plugins\n * provide a post-hook by returning a callback function from configureServer.\n */\nexport async function configureServerPlugins(\n server: Server,\n callback: () => Promise<void>,\n plugins: Plugin[],\n options: ConfigureServerOptions\n) {\n const postHooks: Array<() => void> = [];\n\n // Call the `configureServer()` method for each plugin.\n for (const plugin of plugins) {\n if (plugin.configureServer) {\n const postHook = await plugin.configureServer(server, options);\n if (postHook) {\n postHooks.push(postHook);\n }\n }\n }\n\n // Register any built-in middleware.\n callback();\n\n // Run any post hooks returned by `plugin.configureServer()`.\n for (const postHook of postHooks) {\n await postHook();\n }\n}\n\nexport function getVitePlugins(plugins: Plugin[]): VitePlugin[] {\n const vitePlugins: VitePlugin[] = [];\n for (const plugin of plugins) {\n if (plugin.vitePlugins) {\n vitePlugins.push(...plugin.vitePlugins);\n }\n }\n return vitePlugins;\n}\n"],"mappings":";AA8CA,eAAsB,uBACpB,QACA,UACA,SACA,SACA;AACA,QAAM,YAA+B,CAAC;AAGtC,aAAW,UAAU,SAAS;AAC5B,QAAI,OAAO,iBAAiB;AAC1B,YAAM,WAAW,MAAM,OAAO,gBAAgB,QAAQ,OAAO;AAC7D,UAAI,UAAU;AACZ,kBAAU,KAAK,QAAQ;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAGA,WAAS;AAGT,aAAW,YAAY,WAAW;AAChC,UAAM,SAAS;AAAA,EACjB;AACF;AAEO,SAAS,eAAe,SAAiC;AAC9D,QAAM,cAA4B,CAAC;AACnC,aAAW,UAAU,SAAS;AAC5B,QAAI,OAAO,aAAa;AACtB,kBAAY,KAAK,GAAG,OAAO,WAAW;AAAA,IACxC;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// src/utils/elements.ts
|
|
2
|
+
var ELEMENT_RE = /^[a-z][\w-]*-[\w-]*$/;
|
|
3
|
+
var HTML_ELEMENTS_REGEX = /<([a-z][\w-]*-[\w-]*)/g;
|
|
4
|
+
function isValidTagName(tagName) {
|
|
5
|
+
return ELEMENT_RE.test(tagName);
|
|
6
|
+
}
|
|
7
|
+
function parseTagNames(src) {
|
|
8
|
+
const tagNames = /* @__PURE__ */ new Set();
|
|
9
|
+
const matches = Array.from(src.matchAll(HTML_ELEMENTS_REGEX));
|
|
10
|
+
for (const match of matches) {
|
|
11
|
+
const tagName = match[1];
|
|
12
|
+
tagNames.add(tagName);
|
|
13
|
+
}
|
|
14
|
+
return Array.from(tagNames);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/render/html-minify.ts
|
|
18
|
+
import { createRequire } from "module";
|
|
19
|
+
var require2 = createRequire(import.meta.url);
|
|
20
|
+
var { minify } = require2("html-minifier-terser");
|
|
21
|
+
async function htmlMinify(html, options) {
|
|
22
|
+
const minifyOptions = options || {
|
|
23
|
+
collapseWhitespace: true,
|
|
24
|
+
removeComments: true,
|
|
25
|
+
preserveLineBreaks: true
|
|
26
|
+
};
|
|
27
|
+
try {
|
|
28
|
+
const min = await minify(html, minifyOptions);
|
|
29
|
+
return min.trimStart();
|
|
30
|
+
} catch (e) {
|
|
31
|
+
console.error("failed to minify html:", e);
|
|
32
|
+
return html;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/render/html-pretty.ts
|
|
37
|
+
import { createRequire as createRequire2 } from "module";
|
|
38
|
+
var require3 = createRequire2(import.meta.url);
|
|
39
|
+
var beautify = require3("js-beautify");
|
|
40
|
+
async function htmlPretty(html, options) {
|
|
41
|
+
const prettyOptions = options || {
|
|
42
|
+
indent_size: 0,
|
|
43
|
+
end_with_newline: true,
|
|
44
|
+
extra_liners: []
|
|
45
|
+
};
|
|
46
|
+
try {
|
|
47
|
+
const output = beautify.html(html, prettyOptions);
|
|
48
|
+
return output.trimStart();
|
|
49
|
+
} catch (e) {
|
|
50
|
+
console.error("failed to pretty html:", e);
|
|
51
|
+
return html;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export {
|
|
56
|
+
isValidTagName,
|
|
57
|
+
parseTagNames,
|
|
58
|
+
htmlMinify,
|
|
59
|
+
htmlPretty
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=chunk-GGQGZ7ZE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/elements.ts","../src/render/html-minify.ts","../src/render/html-pretty.ts"],"sourcesContent":["export const ELEMENT_RE = /^[a-z][\\w-]*-[\\w-]*$/;\nexport const HTML_ELEMENTS_REGEX = /<([a-z][\\w-]*-[\\w-]*)/g;\n\n/**\n * Returns true if the tagName is a valid custom element tag.\n */\nexport function isValidTagName(tagName: string) {\n return ELEMENT_RE.test(tagName);\n}\n\n/**\n * Returns a list of custom elements used in a src string (e.g. HTML, jsx, lit).\n * NOTE: the impl uses a simple regex, so tagNames used in comments and attr\n * values may be included.\n */\nexport function parseTagNames(src: string): string[] {\n const tagNames = new Set<string>();\n const matches = Array.from(src.matchAll(HTML_ELEMENTS_REGEX));\n for (const match of matches) {\n const tagName = match[1];\n tagNames.add(tagName);\n }\n return Array.from(tagNames);\n}\n","import {createRequire} from 'module';\nconst require = createRequire(import.meta.url);\nconst {minify} = require('html-minifier-terser');\nimport type {Options} from 'html-minifier-terser';\n\nexport type HtmlMinifyOptions = Options;\n\nexport async function htmlMinify(\n html: string,\n options?: HtmlMinifyOptions\n): Promise<string> {\n const minifyOptions = options || {\n collapseWhitespace: true,\n removeComments: true,\n preserveLineBreaks: true,\n };\n try {\n const min = await minify(html, minifyOptions);\n return min.trimStart();\n } catch (e) {\n console.error('failed to minify html:', e);\n return html;\n }\n}\n","import {createRequire} from 'module';\nconst require = createRequire(import.meta.url);\nconst beautify = require('js-beautify');\nimport type {HTMLBeautifyOptions} from 'js-beautify';\n\nexport type HtmlPrettyOptions = HTMLBeautifyOptions;\n\nexport async function htmlPretty(\n html: string,\n options?: HtmlPrettyOptions\n): Promise<string> {\n const prettyOptions = options || {\n indent_size: 0,\n end_with_newline: true,\n extra_liners: [],\n };\n try {\n const output = beautify.html(html, prettyOptions);\n return output.trimStart();\n } catch (e) {\n console.error('failed to pretty html:', e);\n return html;\n }\n}\n"],"mappings":";AAAO,IAAM,aAAa;AACnB,IAAM,sBAAsB;AAK5B,SAAS,eAAe,SAAiB;AAC9C,SAAO,WAAW,KAAK,OAAO;AAChC;AAOO,SAAS,cAAc,KAAuB;AACnD,QAAM,WAAW,oBAAI,IAAY;AACjC,QAAM,UAAU,MAAM,KAAK,IAAI,SAAS,mBAAmB,CAAC;AAC5D,aAAW,SAAS,SAAS;AAC3B,UAAM,UAAU,MAAM;AACtB,aAAS,IAAI,OAAO;AAAA,EACtB;AACA,SAAO,MAAM,KAAK,QAAQ;AAC5B;;;ACvBA,SAAQ,qBAAoB;AAC5B,IAAMA,WAAU,cAAc,YAAY,GAAG;AAC7C,IAAM,EAAC,OAAM,IAAIA,SAAQ,sBAAsB;AAK/C,eAAsB,WACpB,MACA,SACiB;AACjB,QAAM,gBAAgB,WAAW;AAAA,IAC/B,oBAAoB;AAAA,IACpB,gBAAgB;AAAA,IAChB,oBAAoB;AAAA,EACtB;AACA,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,MAAM,aAAa;AAC5C,WAAO,IAAI,UAAU;AAAA,EACvB,SAAS,GAAP;AACA,YAAQ,MAAM,0BAA0B,CAAC;AACzC,WAAO;AAAA,EACT;AACF;;;ACvBA,SAAQ,iBAAAC,sBAAoB;AAC5B,IAAMC,WAAUD,eAAc,YAAY,GAAG;AAC7C,IAAM,WAAWC,SAAQ,aAAa;AAKtC,eAAsB,WACpB,MACA,SACiB;AACjB,QAAM,gBAAgB,WAAW;AAAA,IAC/B,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,cAAc,CAAC;AAAA,EACjB;AACA,MAAI;AACF,UAAM,SAAS,SAAS,KAAK,MAAM,aAAa;AAChD,WAAO,OAAO,UAAU;AAAA,EAC1B,SAAS,GAAP;AACA,YAAQ,MAAM,0BAA0B,CAAC;AACzC,WAAO;AAAA,EACT;AACF;","names":["require","createRequire","require"]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// src/core/components/Html.tsx
|
|
2
|
+
import { createContext } from "preact";
|
|
3
|
+
import { useContext } from "preact/hooks";
|
|
4
|
+
import { Fragment, jsx } from "preact/jsx-runtime";
|
|
5
|
+
var HTML_CONTEXT = createContext(null);
|
|
6
|
+
var Html = ({ children, ...attrs }) => {
|
|
7
|
+
const context = useContext(HTML_CONTEXT);
|
|
8
|
+
if (!context) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
"HTML_CONTEXT not found, double-check usage of the <Html> component"
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
context.htmlAttrs = attrs;
|
|
14
|
+
return /* @__PURE__ */ jsx(Fragment, {
|
|
15
|
+
children
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// src/core/hooks/useI18nContext.ts
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
import { createContext as createContext2 } from "preact";
|
|
22
|
+
import { useContext as useContext2 } from "preact/hooks";
|
|
23
|
+
var I18N_CONTEXT = createContext2(null);
|
|
24
|
+
function useI18nContext() {
|
|
25
|
+
const context = useContext2(I18N_CONTEXT);
|
|
26
|
+
if (!context) {
|
|
27
|
+
throw new Error("I18N_CONTEXT not found");
|
|
28
|
+
}
|
|
29
|
+
return context;
|
|
30
|
+
}
|
|
31
|
+
function getTranslations(locale) {
|
|
32
|
+
const translations = {};
|
|
33
|
+
const translationsFiles = import.meta.glob(["/translations/*.json"], {
|
|
34
|
+
eager: true
|
|
35
|
+
});
|
|
36
|
+
Object.keys(translationsFiles).forEach((translationPath) => {
|
|
37
|
+
const parts = path.parse(translationPath);
|
|
38
|
+
const locale2 = parts.name;
|
|
39
|
+
const module = translationsFiles[translationPath];
|
|
40
|
+
if (module && module.default) {
|
|
41
|
+
translations[locale2] = module.default;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return translations[locale] || {};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/core/hooks/useRequestContext.ts
|
|
48
|
+
import { createContext as createContext3 } from "preact";
|
|
49
|
+
import { useContext as useContext3 } from "preact/hooks";
|
|
50
|
+
var REQUEST_CONTEXT = createContext3(null);
|
|
51
|
+
function useRequestContext() {
|
|
52
|
+
const context = useContext3(REQUEST_CONTEXT);
|
|
53
|
+
if (!context) {
|
|
54
|
+
throw new Error("REQUEST_CONTEXT not found");
|
|
55
|
+
}
|
|
56
|
+
return context;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export {
|
|
60
|
+
HTML_CONTEXT,
|
|
61
|
+
Html,
|
|
62
|
+
I18N_CONTEXT,
|
|
63
|
+
useI18nContext,
|
|
64
|
+
getTranslations,
|
|
65
|
+
REQUEST_CONTEXT,
|
|
66
|
+
useRequestContext
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=chunk-WTSNW7BB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/components/Html.tsx","../src/core/hooks/useI18nContext.ts","../src/core/hooks/useRequestContext.ts"],"sourcesContent":["import {ComponentChildren, FunctionalComponent, createContext} from 'preact';\nimport {useContext} from 'preact/hooks';\n\nexport interface HtmlContext {\n htmlAttrs: preact.JSX.HTMLAttributes<HTMLHtmlElement>;\n headAttrs: preact.JSX.HTMLAttributes<HTMLHeadElement>;\n headComponents: ComponentChildren[];\n bodyAttrs: preact.JSX.HTMLAttributes<HTMLBodyElement>;\n scriptDeps: Array<preact.JSX.HTMLAttributes<HTMLScriptElement>>;\n}\n\nexport const HTML_CONTEXT = createContext<HtmlContext | null>(null);\n\nexport type HtmlProps = preact.JSX.HTMLAttributes<HTMLHtmlElement> & {\n children?: ComponentChildren;\n};\n\n/**\n * The `<Html>` component can be used to update attrs in the `<html>` tag.\n *\n * Usage:\n *\n * ```tsx\n * <Html lang=\"en-US\">\n * <h1>Hello world</h1>\n * </Html>\n * ```\n */\nexport const Html: FunctionalComponent<HtmlProps> = ({children, ...attrs}) => {\n const context = useContext(HTML_CONTEXT);\n if (!context) {\n throw new Error(\n 'HTML_CONTEXT not found, double-check usage of the <Html> component'\n );\n }\n context.htmlAttrs = attrs;\n return <>{children}</>;\n};\n","import path from 'node:path';\nimport {createContext} from 'preact';\nimport {useContext} from 'preact/hooks';\n\nexport const I18N_CONTEXT = createContext<I18nContext | null>(null);\n\nexport interface I18nContext {\n locale: string;\n translations: Record<string, string>;\n}\n\n/**\n * A hook that returns information about the current i18n context, including the\n * locale for the given route and a map of translations for that locale.\n */\nexport function useI18nContext() {\n const context = useContext(I18N_CONTEXT);\n if (!context) {\n throw new Error('I18N_CONTEXT not found');\n }\n return context;\n}\n\nexport function getTranslations(locale: string): Record<string, string> {\n const translations: Record<string, Record<string, string>> = {};\n const translationsFiles = import.meta.glob(['/translations/*.json'], {\n eager: true,\n }) as Record<string, {default?: Record<string, string>}>;\n Object.keys(translationsFiles).forEach((translationPath) => {\n const parts = path.parse(translationPath);\n const locale = parts.name;\n const module = translationsFiles[translationPath];\n if (module && module.default) {\n translations[locale] = module.default;\n }\n });\n return translations[locale] || {};\n}\n","import {createContext} from 'preact';\nimport {useContext} from 'preact/hooks';\nimport {Route} from '../types';\n\nexport interface RequestContext {\n /** The route file. */\n route: Route;\n /**\n * Route param values. E.g. for a route like `routes/blog/[slug].tsx`,\n * visiting `/blog/foo` will pass {slug: 'foo'} here.\n */\n routeParams: Record<string, string>;\n /** Props passed to the route's server component. */\n props: any;\n /** The current locale. */\n locale: string;\n /** Translations map for the current locale. */\n translations: Record<string, string>;\n}\n\nexport const REQUEST_CONTEXT = createContext<RequestContext | null>(null);\n\n/**\n * A hook that returns information about the current route.\n *\n * Usage:\n *\n * ```ts\n * const ctx = useRequestContext();\n * ctx.route.src;\n * // => 'routes/index.tsx'\n * ```\n */\nexport function useRequestContext() {\n const context = useContext(REQUEST_CONTEXT);\n if (!context) {\n throw new Error('REQUEST_CONTEXT not found');\n }\n return context;\n}\n"],"mappings":";AAAA,SAAgD,qBAAoB;AACpE,SAAQ,kBAAiB;AAmChB;AAzBF,IAAM,eAAe,cAAkC,IAAI;AAiB3D,IAAM,OAAuC,CAAC,EAAC,aAAa,MAAK,MAAM;AAC5E,QAAM,UAAU,WAAW,YAAY;AACvC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,UAAQ,YAAY;AACpB,SAAO;AAAA,IAAG;AAAA,GAAS;AACrB;;;ACrCA,OAAO,UAAU;AACjB,SAAQ,iBAAAA,sBAAoB;AAC5B,SAAQ,cAAAC,mBAAiB;AAElB,IAAM,eAAeD,eAAkC,IAAI;AAW3D,SAAS,iBAAiB;AAC/B,QAAM,UAAUC,YAAW,YAAY;AACvC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AACA,SAAO;AACT;AAEO,SAAS,gBAAgB,QAAwC;AACtE,QAAM,eAAuD,CAAC;AAC9D,QAAM,oBAAoB,YAAY,KAAK,CAAC,sBAAsB,GAAG;AAAA,IACnE,OAAO;AAAA,EACT,CAAC;AACD,SAAO,KAAK,iBAAiB,EAAE,QAAQ,CAAC,oBAAoB;AAC1D,UAAM,QAAQ,KAAK,MAAM,eAAe;AACxC,UAAMC,UAAS,MAAM;AACrB,UAAM,SAAS,kBAAkB;AACjC,QAAI,UAAU,OAAO,SAAS;AAC5B,mBAAaA,WAAU,OAAO;AAAA,IAChC;AAAA,EACF,CAAC;AACD,SAAO,aAAa,WAAW,CAAC;AAClC;;;ACrCA,SAAQ,iBAAAC,sBAAoB;AAC5B,SAAQ,cAAAC,mBAAiB;AAmBlB,IAAM,kBAAkBD,eAAqC,IAAI;AAajE,SAAS,oBAAoB;AAClC,QAAM,UAAUC,YAAW,eAAe;AAC1C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,2BAA2B;AAAA,EAC7C;AACA,SAAO;AACT;","names":["createContext","useContext","locale","createContext","useContext"]}
|