@dxup/nuxt 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -89,8 +89,6 @@ Write top-level named slots in your pages:
89
89
 
90
90
  And them will be forwarded to the active layout automatically.
91
91
 
92
- Due to design limitations, dynamic slots are currently not supported.
93
-
94
92
  ### 4. nitroRoutes
95
93
 
96
94
  Go to definition for nitro routes in data fetching methods.
@@ -20,8 +20,14 @@ const plugin = ({ modules: { typescript: ts, "@vue/compiler-dom": CompilerDOM },
20
20
  if (sfc.scriptSetup) visit(sfc.scriptSetup.ast);
21
21
  function visit(node) {
22
22
  if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "definePageMeta" && node.arguments.length && ts.isObjectLiteralExpression(node.arguments[0])) {
23
- for (const prop of node.arguments[0].properties) if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === "layout" && ts.isStringLiteral(prop.initializer)) {
24
- layoutName = prop.initializer.text;
23
+ for (const prop of node.arguments[0].properties) if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === "layout") {
24
+ if (ts.isStringLiteralLike(prop.initializer)) layoutName = prop.initializer.text;
25
+ else if (ts.isObjectLiteralExpression(prop.initializer)) {
26
+ for (const sub of prop.initializer.properties) if (ts.isPropertyAssignment(sub) && ts.isIdentifier(sub.name) && sub.name.text === "name" && ts.isStringLiteralLike(sub.initializer)) {
27
+ layoutName = sub.initializer.text;
28
+ break;
29
+ }
30
+ }
25
31
  break;
26
32
  }
27
33
  } else ts.forEachChild(node, visit);
package/dist/module.mjs CHANGED
@@ -8,7 +8,6 @@ import { genExport, genImport, genInlineTypeImport, genObjectKey } from "knitwor
8
8
  import { ElementTypes, NodeTypes, parse } from "@vue/compiler-dom";
9
9
  import MagicString from "magic-string";
10
10
  import { createUnplugin } from "unplugin";
11
- import { parseAndWalk } from "oxc-walker";
12
11
  //#region package.json
13
12
  var name = "@dxup/nuxt";
14
13
  //#endregion
@@ -94,72 +93,52 @@ function parseSFC(code) {
94
93
  template
95
94
  };
96
95
  }
96
+ function* forEachElementNode(node) {
97
+ if (node.type === NodeTypes.ROOT || node.type === NodeTypes.FOR || node.type === NodeTypes.IF_BRANCH) for (const child of node.children) yield* forEachElementNode(child);
98
+ else if (node.type === NodeTypes.ELEMENT) {
99
+ yield node;
100
+ for (const child of node.children) yield* forEachElementNode(child);
101
+ } else if (node.type === NodeTypes.IF) for (const branch of node.branches) yield* forEachElementNode(branch);
102
+ }
97
103
  //#endregion
98
- //#region src/module/named-layout-slots/plugins/transform-layout.ts
99
- const TransformLayoutPlugin = (options) => createUnplugin(() => ({
104
+ //#region src/module/named-layout-slots/transform.ts
105
+ const TransformPlugins = (options) => createUnplugin(() => [{
100
106
  name: name + ":transform-layout",
101
107
  enforce: "pre",
102
108
  transformInclude: isVue,
103
- transform: {
104
- filter: { code: /<(?:nuxt-layout|NuxtLayout)/ },
105
- handler(code) {
106
- const { scriptSetup, template } = parseSFC(code);
107
- const layout = template?.children.find((node) => node.type === NodeTypes.ELEMENT && (node.tag === "nuxt-layout" || node.tag === "NuxtLayout"));
108
- if (!layout?.children.length) return;
109
- const s = new MagicString(code);
110
- const prefix = "\n" + genImport("#build/dxup/layouts.mjs", ["LayoutSlot", "provideLayoutSlots"]);
111
- const suffix = `\nprovideLayoutSlots();\n`;
112
- if (scriptSetup) {
113
- s.appendLeft(scriptSetup.innerLoc.start.offset, prefix);
114
- s.appendLeft(scriptSetup.innerLoc.end.offset, suffix);
115
- } else s.prepend(`<script setup>${prefix + suffix}<\/script>\n\n`);
116
- s.appendLeft(layout.children.at(-1).loc.end.offset, `
117
- <template v-for="name in $route.meta.layoutSlots ?? []" :key="name" #[name]="props">
118
- <LayoutSlot :name :props/>
119
- </template>`);
120
- return {
121
- code: s.toString(),
122
- map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
123
- };
124
- }
109
+ transform(code, id) {
110
+ if (!options.layoutDirs.some((dir) => isInDir(id, dir))) return;
111
+ const { scriptSetup, template } = parseSFC(code);
112
+ if (!template) return;
113
+ const slots = [];
114
+ for (const node of forEachElementNode(template)) if (node.tagType === ElementTypes.SLOT && node.props.length && node.props.every((prop) => prop.name !== "name" || prop.type !== NodeTypes.ATTRIBUTE || prop.value && prop.value.content !== "default")) slots.push(node);
115
+ if (!slots.length) return;
116
+ const s = new MagicString(code);
117
+ const imports = genImport("#build/dxup/layouts.mjs", ["LayoutSlot"]);
118
+ if (scriptSetup) {
119
+ const start = scriptSetup.innerLoc.start.offset;
120
+ s.appendLeft(start, `\n${imports}\n`);
121
+ } else s.prepend(`<script setup>\n${imports}\n<\/script>\n\n`);
122
+ for (const slot of slots) for (const offset of /* @__PURE__ */ new Set([slot.loc.start.offset + slot.loc.source.indexOf("slot"), slot.loc.start.offset + slot.loc.source.lastIndexOf("slot")])) s.overwrite(offset, offset + 4, "LayoutSlot");
123
+ return {
124
+ code: s.toString(),
125
+ map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
126
+ };
125
127
  }
126
- }));
127
- //#endregion
128
- //#region src/module/named-layout-slots/plugins/transform-page.ts
129
- const TransformPagePlugin = (options) => createUnplugin(() => ({
128
+ }, {
130
129
  name: name + ":transform-page",
131
130
  enforce: "pre",
132
131
  transformInclude: isVue,
133
132
  transform(code, id) {
134
- if (!options.dirs.some((dir) => isInDir(id, dir))) return;
133
+ if (!options.pageDirs.some((dir) => isInDir(id, dir))) return;
135
134
  const { scriptSetup, template } = parseSFC(code);
136
135
  if (!template) return;
137
- const slots = [];
138
- for (const node of template.children) {
139
- if (node.type !== NodeTypes.ELEMENT || node.tagType !== ElementTypes.TEMPLATE) continue;
140
- const dir = node.props.find((prop) => prop.type === NodeTypes.DIRECTIVE && prop.name === "slot");
141
- if (dir?.arg?.type === NodeTypes.SIMPLE_EXPRESSION && dir.arg.isStatic && dir.arg.content !== "" && dir.arg.content !== "default") slots.push(dir.arg.content);
142
- }
143
- if (!slots.length) return;
144
136
  const s = new MagicString(code);
145
137
  const imports = genImport("#build/dxup/layouts.mjs", ["LayoutSlotsForward"]);
146
- const expression = `layoutSlots: [${slots.map((slot) => JSON.stringify(slot)).join(", ")}],\n`;
147
138
  if (scriptSetup) {
148
- let meta;
149
- parseAndWalk(scriptSetup.innerLoc.source, id, {
150
- parseOptions: { lang: scriptSetup.props.find((prop) => prop.type === NodeTypes.ATTRIBUTE && prop.name === "lang")?.value?.content ?? "ts" },
151
- enter(node) {
152
- if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "definePageMeta" && node.arguments[0]?.type === "ObjectExpression") {
153
- meta = node.arguments[0];
154
- this.skip();
155
- }
156
- }
157
- });
158
139
  const start = scriptSetup.innerLoc.start.offset;
159
140
  s.appendLeft(start, `\n${imports}\n`);
160
- if (meta) s.appendLeft(meta.properties[0].start + start, expression);
161
- else s.appendLeft(scriptSetup.innerLoc.start.offset, `\ndefinePageMeta({\n${expression}});\n`);
162
- } else s.prepend(`<script setup>\n${imports}\ndefinePageMeta({\n${expression}});\n<\/script>\n\n`);
141
+ } else s.prepend(`<script setup>\n${imports}\n<\/script>\n\n`);
163
142
  s.appendLeft(template.innerLoc.start.offset, `<LayoutSlotsForward>`);
164
143
  s.appendLeft(template.innerLoc.end.offset, "</LayoutSlotsForward>");
165
144
  return {
@@ -167,37 +146,54 @@ const TransformPagePlugin = (options) => createUnplugin(() => ({
167
146
  map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
168
147
  };
169
148
  }
170
- }));
149
+ }]);
171
150
  //#endregion
172
151
  //#region src/module/named-layout-slots/module.ts
173
- function setup(nuxt, pluginsVue) {
152
+ async function setup(nuxt, pluginsVue) {
174
153
  const resolver = createResolver(import.meta.url);
154
+ const layoutDirs = nuxt.options._layers.map((layer) => join(layer.config.srcDir, layer.config.dir?.layouts ?? "layouts"));
175
155
  const pageDirs = nuxt.options._layers.map((layer) => join(layer.config.srcDir, layer.config.dir?.pages ?? "pages"));
176
156
  pluginsVue.push({
177
157
  name: "@dxup/nuxt/languages/named-layout-slots.cjs",
178
158
  options: { dirs: pageDirs }
179
159
  });
160
+ nuxt.hook("components:extend", (components) => {
161
+ for (const comp of components) if (comp.pascalName === "NuxtLayout") {
162
+ comp.declarationPath = comp.filePath;
163
+ comp.filePath = join(nuxt.options.buildDir, "dxup/layouts.mjs");
164
+ break;
165
+ }
166
+ });
167
+ const layoutPath = join(await resolver.resolvePath("nuxt", { cwd: nuxt.options.rootDir }), "../app/components/nuxt-layout.js");
180
168
  addTemplate({
181
169
  filename: "dxup/layouts.mjs",
182
170
  getContents() {
183
- return genExport(resolver.resolve("runtime/layouts.mjs"), "*");
171
+ return [
172
+ genExport(resolver.resolve("runtime/layouts.mjs"), "*"),
173
+ genExport(resolver.resolve("runtime/layouts.mjs"), ["default"]),
174
+ genExport(layoutPath, [{
175
+ name: "default",
176
+ as: "NuxtLayout"
177
+ }])
178
+ ].join("\n");
184
179
  }
185
180
  });
186
181
  addTypeTemplate({
187
182
  filename: "dxup/layouts.d.ts",
188
183
  getContents({ app }) {
184
+ const currentDir = join(nuxt.options.buildDir, "dxup");
189
185
  return `
190
186
  export interface Layouts {
191
- ${Object.values(app.layouts).map((layout) => ` ${genObjectKey(layout.name)}: ${genInlineTypeImport(layout.file)};`).join("\n")}
187
+ ${Object.values(app.layouts).map((layout) => ` ${genObjectKey(layout.name)}: ${genInlineTypeImport(relative(currentDir, layout.file))};`).join("\n")}
192
188
  }
193
189
  `.trimStart();
194
190
  }
195
191
  });
196
- addBuildPlugin(TransformPagePlugin({
197
- dirs: pageDirs,
192
+ addBuildPlugin(TransformPlugins({
193
+ layoutDirs,
194
+ pageDirs,
198
195
  sourcemap: !!nuxt.options.sourcemap.client
199
196
  }));
200
- addBuildPlugin(TransformLayoutPlugin({ sourcemap: !!nuxt.options.sourcemap.client }));
201
197
  }
202
198
  //#endregion
203
199
  //#region src/module/index.ts
@@ -220,7 +216,7 @@ var module_default = defineNuxtModule().with({
220
216
  async setup(options, nuxt) {
221
217
  const pluginsTs = [{ name: "@dxup/nuxt" }];
222
218
  const pluginsVue = [];
223
- if (options.features.namedLayoutSlots) setup(nuxt, pluginsVue);
219
+ if (options.features.namedLayoutSlots) await setup(nuxt, pluginsVue);
224
220
  if (options.features.unimport) pluginsTs.unshift({ name: "@dxup/unimport" });
225
221
  append(pluginsTs, nuxt.options, "typescript", "tsConfig", "compilerOptions");
226
222
  append(pluginsTs, nuxt.options.nitro, "typescript", "tsConfig", "compilerOptions");
@@ -1,21 +1,10 @@
1
- import { ShallowRef, Slots } from "vue";
2
-
3
1
  //#region src/module/named-layout-slots/runtime/layouts.d.ts
4
- interface LayoutSlotsRegistry {
5
- slots: ShallowRef<Slots>;
6
- set: (slots: Slots) => void;
7
- waitFor: (name: string) => Promise<void>;
8
- }
9
- declare function provideLayoutSlots(): LayoutSlotsRegistry;
2
+ declare const _default: import("vue").DefineSetupFnComponent<Record<string, any>, {}, {}, Record<string, any> & {}, import("vue").PublicProps>;
10
3
  declare const LayoutSlot: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
11
4
  name: {
12
5
  type: StringConstructor;
13
6
  required: true;
14
7
  };
15
- props: {
16
- type: ObjectConstructor;
17
- default: () => {};
18
- };
19
8
  }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
20
9
  [key: string]: any;
21
10
  }>[] | undefined, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
@@ -23,13 +12,7 @@ declare const LayoutSlot: import("vue").DefineComponent<import("vue").ExtractPro
23
12
  type: StringConstructor;
24
13
  required: true;
25
14
  };
26
- props: {
27
- type: ObjectConstructor;
28
- default: () => {};
29
- };
30
- }>> & Readonly<{}>, {
31
- props: Record<string, any>;
32
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
15
+ }>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
33
16
  declare const LayoutSlotsForward: import("vue").DefineSetupFnComponent<Record<string, any>, {}, {}, Record<string, any> & {}, import("vue").PublicProps>;
34
17
  //#endregion
35
- export { LayoutSlot, LayoutSlotsForward, provideLayoutSlots };
18
+ export { LayoutSlot, LayoutSlotsForward, _default as default };
@@ -1,53 +1,38 @@
1
- import { defineComponent, inject, provide, shallowRef } from "vue";
1
+ import { defineComponent, h, inject, provide, shallowRef } from "vue";
2
+ import { NuxtLayout } from "#build/dxup/layouts.mjs";
2
3
  //#region src/module/named-layout-slots/runtime/layouts.ts
3
4
  const injectionKey = Symbol();
4
- function provideLayoutSlots() {
5
+ var layouts_default = defineComponent((props, ctx) => {
5
6
  const slots = shallowRef({});
6
- const waiters = /* @__PURE__ */ new Map();
7
- const registry = {
7
+ let resolveReady;
8
+ provide(injectionKey, {
8
9
  slots,
10
+ ready: new Promise((resolve) => {
11
+ resolveReady = resolve;
12
+ }),
9
13
  set(value) {
10
14
  slots.value = value;
11
- for (const name of Object.keys(value)) {
12
- const resolves = waiters.get(name);
13
- if (!resolves?.length) continue;
14
- waiters.delete(name);
15
- for (const resolve of resolves) resolve();
16
- }
17
- },
18
- waitFor(name) {
19
- if (slots.value[name]) return Promise.resolve();
20
- return new Promise((resolve) => {
21
- let resolves = waiters.get(name);
22
- if (!resolves) waiters.set(name, resolves = []);
23
- resolves.push(resolve);
24
- });
15
+ resolveReady?.();
25
16
  }
26
- };
27
- provide(injectionKey, registry);
28
- return registry;
29
- }
17
+ });
18
+ return () => h(NuxtLayout, props, ctx.slots);
19
+ });
30
20
  const LayoutSlot = defineComponent({
31
- props: {
32
- name: {
33
- type: String,
34
- required: true
35
- },
36
- props: {
37
- type: Object,
38
- default: () => ({})
39
- }
40
- },
41
- setup(props) {
42
- const registry = inject(injectionKey);
43
- const render = () => registry.slots.value[props.name]?.(props.props);
44
- if (import.meta.server && !registry.slots.value[props.name]) return registry.waitFor(props.name).then(() => render);
21
+ props: { name: {
22
+ type: String,
23
+ required: true
24
+ } },
25
+ setup(props, ctx) {
26
+ const { slots, ready } = inject(injectionKey);
27
+ const render = () => slots.value[props.name]?.(ctx.attrs);
28
+ if (import.meta.server && !slots.value[props.name]) return ready.then(() => render);
45
29
  return render;
46
30
  }
47
31
  });
48
32
  const LayoutSlotsForward = defineComponent((props, ctx) => {
49
- inject(injectionKey).set(ctx.slots);
33
+ const { set } = inject(injectionKey);
34
+ set(ctx.slots);
50
35
  return () => ctx.slots.default?.();
51
36
  });
52
37
  //#endregion
53
- export { LayoutSlot, LayoutSlotsForward, provideLayoutSlots };
38
+ export { LayoutSlot, LayoutSlotsForward, layouts_default as default };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dxup/nuxt",
3
3
  "type": "module",
4
- "version": "0.5.1",
4
+ "version": "0.5.2",
5
5
  "description": "TypeScript plugin for Nuxt",
6
6
  "author": "KazariEX",
7
7
  "license": "MIT",
@@ -25,7 +25,6 @@
25
25
  "chokidar": "^5.0.0",
26
26
  "knitwork": "^1.3.0",
27
27
  "magic-string": "^0.30.21",
28
- "oxc-walker": "^1.0.0",
29
28
  "pathe": "^2.0.3",
30
29
  "tinyglobby": "^0.2.17",
31
30
  "unplugin": "^3.0.0",