@meteor-vite/plugin-zodern-relay 1.0.0 → 1.0.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 +55 -0
- package/dist/Plugin.d.mts +16 -2
- package/dist/Plugin.d.ts +16 -2
- package/dist/Plugin.js +57 -10
- package/dist/Plugin.js.map +1 -1
- package/dist/Plugin.mjs +47 -10
- package/dist/Plugin.mjs.map +1 -1
- package/package.json +6 -1
- package/src/Plugin.ts +76 -12
- package/stubs/babel-plugin.js +445 -0
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Vite plugin `zodern:relay`
|
|
2
|
+
|
|
3
|
+
This is a Vite compatability package for
|
|
4
|
+
[`zodern:relay`](https://github.com/zodern/meteor-relay#readme) - type safe
|
|
5
|
+
[Meteor](https://meteor.com/) methods and publications.
|
|
6
|
+
|
|
7
|
+
This plugin acts as partial replacement for the Babel `@zodern/babel-plugin-meteor-relay` plugin required by
|
|
8
|
+
`zodern:relay`. You still need the Babel plugin as it might still be required on the server.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
```sh
|
|
13
|
+
npm i -D @meteor-vite/plugin-zodern-relay
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Configuration
|
|
17
|
+
Add the plugin to your Vite config and you're all set. If your methods and publications reside outside of `imports/api/<methods|publications>`, specify those paths when calling the plugin.
|
|
18
|
+
```ts
|
|
19
|
+
// vite.config.ts
|
|
20
|
+
import zodernRelay from '@meteor-vite/plugin-zodern-relay';
|
|
21
|
+
import { meteor } from 'meteor-vite/plugin';
|
|
22
|
+
|
|
23
|
+
export default defineConfig({
|
|
24
|
+
plugins: [
|
|
25
|
+
meteor({
|
|
26
|
+
clientEntry: '...',
|
|
27
|
+
}),
|
|
28
|
+
zodernRelay({
|
|
29
|
+
directories: {
|
|
30
|
+
/**
|
|
31
|
+
* Path to directories where your zodern:relay methods live
|
|
32
|
+
* @default ['./imports/methods']
|
|
33
|
+
*/
|
|
34
|
+
methods: ['./imports/methods'],
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Path to the directories where your zodern:relay publications live.
|
|
38
|
+
* @default ['./imports/publications']
|
|
39
|
+
*/
|
|
40
|
+
publications: ['./imports/publications'],
|
|
41
|
+
}
|
|
42
|
+
}),
|
|
43
|
+
]
|
|
44
|
+
})
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Usage & Documentation
|
|
48
|
+
You can use [`zodern:relay`](https://github.com/zodern/meteor-relay#readme) like you normally would. Consult their
|
|
49
|
+
readme for documentation.
|
|
50
|
+
|
|
51
|
+
- `zodern:relay` - https://github.com/zodern/meteor-relay#readme
|
|
52
|
+
- `meteor-vite` - https://github.com/JorgenVatle/meteor-vite#readme
|
|
53
|
+
|
|
54
|
+
## License
|
|
55
|
+
MIT
|
package/dist/Plugin.d.mts
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
2
|
|
|
3
|
-
declare function zodernRelay(): Promise<Plugin>;
|
|
3
|
+
declare function zodernRelay(options?: Options): Promise<Plugin>;
|
|
4
|
+
interface Options {
|
|
5
|
+
directories?: {
|
|
6
|
+
/**
|
|
7
|
+
* Path to directories where your zodern:relay methods live
|
|
8
|
+
* @default ['./imports/methods']
|
|
9
|
+
*/
|
|
10
|
+
methods?: string[];
|
|
11
|
+
/**
|
|
12
|
+
* Path to the directories where your zodern:relay publications live.
|
|
13
|
+
* @default ['./imports/publications']
|
|
14
|
+
*/
|
|
15
|
+
publications?: string[];
|
|
16
|
+
};
|
|
17
|
+
}
|
|
4
18
|
|
|
5
|
-
export { zodernRelay as default };
|
|
19
|
+
export { type Options, zodernRelay as default };
|
package/dist/Plugin.d.ts
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
2
|
|
|
3
|
-
declare function zodernRelay(): Promise<Plugin>;
|
|
3
|
+
declare function zodernRelay(options?: Options): Promise<Plugin>;
|
|
4
|
+
interface Options {
|
|
5
|
+
directories?: {
|
|
6
|
+
/**
|
|
7
|
+
* Path to directories where your zodern:relay methods live
|
|
8
|
+
* @default ['./imports/methods']
|
|
9
|
+
*/
|
|
10
|
+
methods?: string[];
|
|
11
|
+
/**
|
|
12
|
+
* Path to the directories where your zodern:relay publications live.
|
|
13
|
+
* @default ['./imports/publications']
|
|
14
|
+
*/
|
|
15
|
+
publications?: string[];
|
|
16
|
+
};
|
|
17
|
+
}
|
|
4
18
|
|
|
5
|
-
export { zodernRelay as default };
|
|
19
|
+
export { type Options, zodernRelay as default };
|
package/dist/Plugin.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/Plugin.ts
|
|
@@ -23,22 +33,59 @@ __export(Plugin_exports, {
|
|
|
23
33
|
default: () => zodernRelay
|
|
24
34
|
});
|
|
25
35
|
module.exports = __toCommonJS(Plugin_exports);
|
|
26
|
-
var
|
|
27
|
-
var
|
|
28
|
-
|
|
36
|
+
var import_core = require("@babel/core");
|
|
37
|
+
var import_fs = __toESM(require("fs"));
|
|
38
|
+
var import_path = __toESM(require("path"));
|
|
39
|
+
var cwd = process.cwd();
|
|
40
|
+
async function zodernRelay(options) {
|
|
41
|
+
const config = {
|
|
42
|
+
directories: {
|
|
43
|
+
methods: options?.directories?.methods || ["./imports/methods"],
|
|
44
|
+
publications: options?.directories?.publications || ["./imports/publications"]
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const directories = [
|
|
48
|
+
...config.directories.methods.map((path) => ["methods", import_path.default.relative(cwd, path)]),
|
|
49
|
+
...config.directories.publications.map((path) => ["publications", import_path.default.relative(cwd, path)])
|
|
50
|
+
];
|
|
51
|
+
function resolveRelay(id) {
|
|
52
|
+
const relativePath = import_path.default.relative(cwd, id);
|
|
53
|
+
for (const [type, directory] of directories) {
|
|
54
|
+
if (!relativePath.startsWith(directory)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
id,
|
|
59
|
+
type,
|
|
60
|
+
relativePath
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
29
64
|
return {
|
|
30
65
|
name: "zodern-relay",
|
|
31
|
-
|
|
32
|
-
|
|
66
|
+
async load(filename) {
|
|
67
|
+
const relay = resolveRelay(filename || "");
|
|
68
|
+
if (!relay) {
|
|
33
69
|
return;
|
|
34
70
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
71
|
+
const code = import_fs.default.readFileSync(filename, "utf-8");
|
|
72
|
+
const transform = await (0, import_core.transformAsync)(code, {
|
|
73
|
+
configFile: false,
|
|
74
|
+
babelrc: false,
|
|
75
|
+
filename,
|
|
76
|
+
plugins: ["@zodern/babel-plugin-meteor-relay"],
|
|
77
|
+
caller: {
|
|
78
|
+
name: "@meteor-vite/plugin-zodern-relay",
|
|
79
|
+
// @ts-expect-error No type definition for this, but it's required by the Babel plugin.
|
|
80
|
+
arch: "web.browser.vite"
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
if (!transform) {
|
|
39
84
|
return;
|
|
40
85
|
}
|
|
41
|
-
return
|
|
86
|
+
return {
|
|
87
|
+
code: transform.code ?? ""
|
|
88
|
+
};
|
|
42
89
|
}
|
|
43
90
|
};
|
|
44
91
|
}
|
package/dist/Plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Plugin.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"sources":["../src/Plugin.ts"],"sourcesContent":["import { transformAsync } from '@babel/core';\nimport FS from 'fs';\nimport Path from 'path';\nimport { type Plugin } from 'vite';\n\nconst cwd = process.cwd();\n\nexport default async function zodernRelay(options?: Options): Promise<Plugin> {\n const config = {\n directories: {\n methods: options?.directories?.methods || ['./imports/methods'],\n publications: options?.directories?.publications || ['./imports/publications'],\n }\n } satisfies Options;\n \n const directories = [\n ...config.directories.methods.map((path) => ['methods', Path.relative(cwd, path)]),\n ...config.directories.publications.map((path) => ['publications', Path.relative(cwd, path)])\n ] as [RelayInfo['type'], string][];\n \n function resolveRelay(id: string): RelayInfo | undefined {\n const relativePath = Path.relative(cwd, id);\n for (const [type, directory] of directories) {\n if (!relativePath.startsWith(directory)) {\n continue;\n }\n return {\n id,\n type,\n relativePath,\n }\n }\n }\n \n return {\n name: 'zodern-relay',\n async load(filename) {\n const relay = resolveRelay(filename || '');\n if (!relay) {\n return;\n }\n const code = FS.readFileSync(filename, 'utf-8');\n const transform = await transformAsync(code, {\n configFile: false,\n babelrc: false,\n filename,\n plugins: ['@zodern/babel-plugin-meteor-relay'],\n caller: {\n name: '@meteor-vite/plugin-zodern-relay',\n \n // @ts-expect-error No type definition for this, but it's required by the Babel plugin.\n arch: 'web.browser.vite',\n }\n });\n \n if (!transform) {\n return;\n }\n \n return {\n code: transform.code ?? '',\n }\n }\n }\n \n \n}\nexport interface Options {\n directories?: {\n /**\n * Path to directories where your zodern:relay methods live\n * @default ['./imports/methods']\n */\n methods?: string[],\n \n /**\n * Path to the directories where your zodern:relay publications live.\n * @default ['./imports/publications']\n */\n publications?: string[],\n }\n}\n\ntype RelayInfo = {\n type: 'methods' | 'publications';\n id: string;\n relativePath: string;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAA+B;AAC/B,gBAAe;AACf,kBAAiB;AAGjB,IAAM,MAAM,QAAQ,IAAI;AAExB,eAAO,YAAmC,SAAoC;AAC1E,QAAM,SAAS;AAAA,IACX,aAAa;AAAA,MACT,SAAS,SAAS,aAAa,WAAW,CAAC,mBAAmB;AAAA,MAC9D,cAAc,SAAS,aAAa,gBAAgB,CAAC,wBAAwB;AAAA,IACjF;AAAA,EACJ;AAEA,QAAM,cAAc;AAAA,IAChB,GAAG,OAAO,YAAY,QAAQ,IAAI,CAAC,SAAS,CAAC,WAAW,YAAAA,QAAK,SAAS,KAAK,IAAI,CAAC,CAAC;AAAA,IACjF,GAAG,OAAO,YAAY,aAAa,IAAI,CAAC,SAAS,CAAC,gBAAgB,YAAAA,QAAK,SAAS,KAAK,IAAI,CAAC,CAAC;AAAA,EAC/F;AAEA,WAAS,aAAa,IAAmC;AACrD,UAAM,eAAe,YAAAA,QAAK,SAAS,KAAK,EAAE;AAC1C,eAAW,CAAC,MAAM,SAAS,KAAK,aAAa;AACzC,UAAI,CAAC,aAAa,WAAW,SAAS,GAAG;AACrC;AAAA,MACJ;AACA,aAAO;AAAA,QACH;AAAA,QACA;AAAA,QACA;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAEA,SAAO;AAAA,IACH,MAAM;AAAA,IACN,MAAM,KAAK,UAAU;AACjB,YAAM,QAAQ,aAAa,YAAY,EAAE;AACzC,UAAI,CAAC,OAAO;AACR;AAAA,MACJ;AACA,YAAM,OAAO,UAAAC,QAAG,aAAa,UAAU,OAAO;AAC9C,YAAM,YAAY,UAAM,4BAAe,MAAM;AAAA,QACzC,YAAY;AAAA,QACZ,SAAS;AAAA,QACT;AAAA,QACA,SAAS,CAAC,mCAAmC;AAAA,QAC7C,QAAQ;AAAA,UACJ,MAAM;AAAA;AAAA,UAGN,MAAM;AAAA,QACV;AAAA,MACJ,CAAC;AAED,UAAI,CAAC,WAAW;AACZ;AAAA,MACJ;AAEA,aAAO;AAAA,QACH,MAAM,UAAU,QAAQ;AAAA,MAC5B;AAAA,IACJ;AAAA,EACJ;AAGJ;","names":["Path","FS"]}
|
package/dist/Plugin.mjs
CHANGED
|
@@ -1,20 +1,57 @@
|
|
|
1
1
|
// src/Plugin.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { transformAsync } from "@babel/core";
|
|
3
|
+
import FS from "fs";
|
|
4
|
+
import Path from "path";
|
|
5
|
+
var cwd = process.cwd();
|
|
6
|
+
async function zodernRelay(options) {
|
|
7
|
+
const config = {
|
|
8
|
+
directories: {
|
|
9
|
+
methods: options?.directories?.methods || ["./imports/methods"],
|
|
10
|
+
publications: options?.directories?.publications || ["./imports/publications"]
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const directories = [
|
|
14
|
+
...config.directories.methods.map((path) => ["methods", Path.relative(cwd, path)]),
|
|
15
|
+
...config.directories.publications.map((path) => ["publications", Path.relative(cwd, path)])
|
|
16
|
+
];
|
|
17
|
+
function resolveRelay(id) {
|
|
18
|
+
const relativePath = Path.relative(cwd, id);
|
|
19
|
+
for (const [type, directory] of directories) {
|
|
20
|
+
if (!relativePath.startsWith(directory)) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
id,
|
|
25
|
+
type,
|
|
26
|
+
relativePath
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
5
30
|
return {
|
|
6
31
|
name: "zodern-relay",
|
|
7
|
-
|
|
8
|
-
|
|
32
|
+
async load(filename) {
|
|
33
|
+
const relay = resolveRelay(filename || "");
|
|
34
|
+
if (!relay) {
|
|
9
35
|
return;
|
|
10
36
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
37
|
+
const code = FS.readFileSync(filename, "utf-8");
|
|
38
|
+
const transform = await transformAsync(code, {
|
|
39
|
+
configFile: false,
|
|
40
|
+
babelrc: false,
|
|
41
|
+
filename,
|
|
42
|
+
plugins: ["@zodern/babel-plugin-meteor-relay"],
|
|
43
|
+
caller: {
|
|
44
|
+
name: "@meteor-vite/plugin-zodern-relay",
|
|
45
|
+
// @ts-expect-error No type definition for this, but it's required by the Babel plugin.
|
|
46
|
+
arch: "web.browser.vite"
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
if (!transform) {
|
|
15
50
|
return;
|
|
16
51
|
}
|
|
17
|
-
return
|
|
52
|
+
return {
|
|
53
|
+
code: transform.code ?? ""
|
|
54
|
+
};
|
|
18
55
|
}
|
|
19
56
|
};
|
|
20
57
|
}
|
package/dist/Plugin.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Plugin.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"sources":["../src/Plugin.ts"],"sourcesContent":["import { transformAsync } from '@babel/core';\nimport FS from 'fs';\nimport Path from 'path';\nimport { type Plugin } from 'vite';\n\nconst cwd = process.cwd();\n\nexport default async function zodernRelay(options?: Options): Promise<Plugin> {\n const config = {\n directories: {\n methods: options?.directories?.methods || ['./imports/methods'],\n publications: options?.directories?.publications || ['./imports/publications'],\n }\n } satisfies Options;\n \n const directories = [\n ...config.directories.methods.map((path) => ['methods', Path.relative(cwd, path)]),\n ...config.directories.publications.map((path) => ['publications', Path.relative(cwd, path)])\n ] as [RelayInfo['type'], string][];\n \n function resolveRelay(id: string): RelayInfo | undefined {\n const relativePath = Path.relative(cwd, id);\n for (const [type, directory] of directories) {\n if (!relativePath.startsWith(directory)) {\n continue;\n }\n return {\n id,\n type,\n relativePath,\n }\n }\n }\n \n return {\n name: 'zodern-relay',\n async load(filename) {\n const relay = resolveRelay(filename || '');\n if (!relay) {\n return;\n }\n const code = FS.readFileSync(filename, 'utf-8');\n const transform = await transformAsync(code, {\n configFile: false,\n babelrc: false,\n filename,\n plugins: ['@zodern/babel-plugin-meteor-relay'],\n caller: {\n name: '@meteor-vite/plugin-zodern-relay',\n \n // @ts-expect-error No type definition for this, but it's required by the Babel plugin.\n arch: 'web.browser.vite',\n }\n });\n \n if (!transform) {\n return;\n }\n \n return {\n code: transform.code ?? '',\n }\n }\n }\n \n \n}\nexport interface Options {\n directories?: {\n /**\n * Path to directories where your zodern:relay methods live\n * @default ['./imports/methods']\n */\n methods?: string[],\n \n /**\n * Path to the directories where your zodern:relay publications live.\n * @default ['./imports/publications']\n */\n publications?: string[],\n }\n}\n\ntype RelayInfo = {\n type: 'methods' | 'publications';\n id: string;\n relativePath: string;\n};\n"],"mappings":";AAAA,SAAS,sBAAsB;AAC/B,OAAO,QAAQ;AACf,OAAO,UAAU;AAGjB,IAAM,MAAM,QAAQ,IAAI;AAExB,eAAO,YAAmC,SAAoC;AAC1E,QAAM,SAAS;AAAA,IACX,aAAa;AAAA,MACT,SAAS,SAAS,aAAa,WAAW,CAAC,mBAAmB;AAAA,MAC9D,cAAc,SAAS,aAAa,gBAAgB,CAAC,wBAAwB;AAAA,IACjF;AAAA,EACJ;AAEA,QAAM,cAAc;AAAA,IAChB,GAAG,OAAO,YAAY,QAAQ,IAAI,CAAC,SAAS,CAAC,WAAW,KAAK,SAAS,KAAK,IAAI,CAAC,CAAC;AAAA,IACjF,GAAG,OAAO,YAAY,aAAa,IAAI,CAAC,SAAS,CAAC,gBAAgB,KAAK,SAAS,KAAK,IAAI,CAAC,CAAC;AAAA,EAC/F;AAEA,WAAS,aAAa,IAAmC;AACrD,UAAM,eAAe,KAAK,SAAS,KAAK,EAAE;AAC1C,eAAW,CAAC,MAAM,SAAS,KAAK,aAAa;AACzC,UAAI,CAAC,aAAa,WAAW,SAAS,GAAG;AACrC;AAAA,MACJ;AACA,aAAO;AAAA,QACH;AAAA,QACA;AAAA,QACA;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAEA,SAAO;AAAA,IACH,MAAM;AAAA,IACN,MAAM,KAAK,UAAU;AACjB,YAAM,QAAQ,aAAa,YAAY,EAAE;AACzC,UAAI,CAAC,OAAO;AACR;AAAA,MACJ;AACA,YAAM,OAAO,GAAG,aAAa,UAAU,OAAO;AAC9C,YAAM,YAAY,MAAM,eAAe,MAAM;AAAA,QACzC,YAAY;AAAA,QACZ,SAAS;AAAA,QACT;AAAA,QACA,SAAS,CAAC,mCAAmC;AAAA,QAC7C,QAAQ;AAAA,UACJ,MAAM;AAAA;AAAA,UAGN,MAAM;AAAA,QACV;AAAA,MACJ,CAAC;AAED,UAAI,CAAC,WAAW;AACZ;AAAA,MACJ;AAEA,aAAO;AAAA,QACH,MAAM,UAAU,QAAQ;AAAA,MAC5B;AAAA,IACJ;AAAA,EACJ;AAGJ;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meteor-vite/plugin-zodern-relay",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Vite compatability plugin for zodern:relay - typed Meteor methods and publications",
|
|
5
5
|
"main": "dist/Plugin.js",
|
|
6
6
|
"exports": {
|
|
@@ -40,8 +40,13 @@
|
|
|
40
40
|
"url": "https://github.com/JorgenVatle/meteor-vite"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
+
"@types/babel__core": "^7.20.5",
|
|
43
44
|
"tsup": "^8.0.2",
|
|
44
45
|
"typescript": "^5.4.2",
|
|
45
46
|
"vite": "^5.1.6"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"@babel/core": "^7.0.0",
|
|
50
|
+
"vite": ">=3.0.0"
|
|
46
51
|
}
|
|
47
52
|
}
|
package/src/Plugin.ts
CHANGED
|
@@ -1,24 +1,88 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { transformAsync } from '@babel/core';
|
|
2
|
+
import FS from 'fs';
|
|
3
|
+
import Path from 'path';
|
|
4
|
+
import { type Plugin } from 'vite';
|
|
2
5
|
|
|
3
|
-
const
|
|
4
|
-
const stubModule = '@meteor-vite/zodern-relay/stubs/relay-client';
|
|
6
|
+
const cwd = process.cwd();
|
|
5
7
|
|
|
6
|
-
export default async function zodernRelay(): Promise<Plugin> {
|
|
8
|
+
export default async function zodernRelay(options?: Options): Promise<Plugin> {
|
|
9
|
+
const config = {
|
|
10
|
+
directories: {
|
|
11
|
+
methods: options?.directories?.methods || ['./imports/methods'],
|
|
12
|
+
publications: options?.directories?.publications || ['./imports/publications'],
|
|
13
|
+
}
|
|
14
|
+
} satisfies Options;
|
|
15
|
+
|
|
16
|
+
const directories = [
|
|
17
|
+
...config.directories.methods.map((path) => ['methods', Path.relative(cwd, path)]),
|
|
18
|
+
...config.directories.publications.map((path) => ['publications', Path.relative(cwd, path)])
|
|
19
|
+
] as [RelayInfo['type'], string][];
|
|
20
|
+
|
|
21
|
+
function resolveRelay(id: string): RelayInfo | undefined {
|
|
22
|
+
const relativePath = Path.relative(cwd, id);
|
|
23
|
+
for (const [type, directory] of directories) {
|
|
24
|
+
if (!relativePath.startsWith(directory)) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
id,
|
|
29
|
+
type,
|
|
30
|
+
relativePath,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
7
35
|
return {
|
|
8
36
|
name: 'zodern-relay',
|
|
9
|
-
|
|
10
|
-
|
|
37
|
+
async load(filename) {
|
|
38
|
+
const relay = resolveRelay(filename || '');
|
|
39
|
+
if (!relay) {
|
|
11
40
|
return;
|
|
12
41
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
42
|
+
const code = FS.readFileSync(filename, 'utf-8');
|
|
43
|
+
const transform = await transformAsync(code, {
|
|
44
|
+
configFile: false,
|
|
45
|
+
babelrc: false,
|
|
46
|
+
filename,
|
|
47
|
+
plugins: ['@zodern/babel-plugin-meteor-relay'],
|
|
48
|
+
caller: {
|
|
49
|
+
name: '@meteor-vite/plugin-zodern-relay',
|
|
50
|
+
|
|
51
|
+
// @ts-expect-error No type definition for this, but it's required by the Babel plugin.
|
|
52
|
+
arch: 'web.browser.vite',
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (!transform) {
|
|
17
57
|
return;
|
|
18
58
|
}
|
|
19
59
|
|
|
20
|
-
|
|
21
|
-
|
|
60
|
+
return {
|
|
61
|
+
code: transform.code ?? '',
|
|
62
|
+
}
|
|
22
63
|
}
|
|
23
64
|
}
|
|
65
|
+
|
|
66
|
+
|
|
24
67
|
}
|
|
68
|
+
export interface Options {
|
|
69
|
+
directories?: {
|
|
70
|
+
/**
|
|
71
|
+
* Path to directories where your zodern:relay methods live
|
|
72
|
+
* @default ['./imports/methods']
|
|
73
|
+
*/
|
|
74
|
+
methods?: string[],
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Path to the directories where your zodern:relay publications live.
|
|
78
|
+
* @default ['./imports/publications']
|
|
79
|
+
*/
|
|
80
|
+
publications?: string[],
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
type RelayInfo = {
|
|
85
|
+
type: 'methods' | 'publications';
|
|
86
|
+
id: string;
|
|
87
|
+
relativePath: string;
|
|
88
|
+
};
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
const { createHash } = require('crypto');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
// If has a MemberExpression, returns the first call expression in its callee
|
|
5
|
+
// Otherwise, returns the call expression
|
|
6
|
+
function getFirstCallExpr(call) {
|
|
7
|
+
if (call.node.callee.type !== 'MemberExpression') {
|
|
8
|
+
return call;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
if (
|
|
13
|
+
call.node.callee.object.type !== 'CallExpression'
|
|
14
|
+
) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return call.get('callee.object');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = function (api) {
|
|
22
|
+
let t = api.types;
|
|
23
|
+
|
|
24
|
+
let caller;
|
|
25
|
+
api.caller(function (c) {
|
|
26
|
+
caller = c;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
function createExport(exportName, callee, name, stub) {
|
|
30
|
+
let args = [
|
|
31
|
+
t.StringLiteral(name)
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
if (stub) {
|
|
35
|
+
if (stub.type === 'ObjectMethod') {
|
|
36
|
+
stub = t.FunctionExpression(
|
|
37
|
+
null,
|
|
38
|
+
stub.node.params || [],
|
|
39
|
+
stub.node.body,
|
|
40
|
+
stub.node.generator,
|
|
41
|
+
stub.node.async
|
|
42
|
+
)
|
|
43
|
+
} else if (stub.type === 'ObjectProperty') {
|
|
44
|
+
stub = stub.node.value;
|
|
45
|
+
}
|
|
46
|
+
args.push(stub);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const declaration = t.CallExpression(
|
|
50
|
+
t.Identifier(callee),
|
|
51
|
+
args
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
if (exportName === null) {
|
|
55
|
+
return t.ExportDefaultDeclaration(
|
|
56
|
+
declaration
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return t.ExportNamedDeclaration(
|
|
61
|
+
t.VariableDeclaration(
|
|
62
|
+
'const',
|
|
63
|
+
[t.VariableDeclarator(
|
|
64
|
+
t.Identifier(exportName),
|
|
65
|
+
declaration
|
|
66
|
+
)]
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function getOrAddName(args, { exportName, filePath, isPub }) {
|
|
72
|
+
if (args[0].type !== 'ObjectExpression') {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let obj = args[0];
|
|
77
|
+
let nameProperty = obj.properties.find((property) => {
|
|
78
|
+
if (property.key.type !== 'Identifier') {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (property.key.name !== 'name') {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return property.value.type === 'StringLiteral';
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
if (nameProperty) {
|
|
90
|
+
return nameProperty.value.value;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let fileHash = 'M' + createHash('sha256')
|
|
94
|
+
.update(filePath)
|
|
95
|
+
.digest('hex')
|
|
96
|
+
.substring(0, 5);
|
|
97
|
+
|
|
98
|
+
let name = exportName;
|
|
99
|
+
|
|
100
|
+
if (name === null) {
|
|
101
|
+
let baseName = path.basename(filePath);
|
|
102
|
+
let lastDotIndex = baseName.lastIndexOf('.');
|
|
103
|
+
name = baseName.substring(0, lastDotIndex);
|
|
104
|
+
} else if (isPub && name.startsWith('subscribe')) {
|
|
105
|
+
name = exportName.substring('subscribe'.length);
|
|
106
|
+
|
|
107
|
+
if (name[0] !== name[0].toLowerCase()) {
|
|
108
|
+
name = `${name[0].toLowerCase()}${name.substring(1)}`
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
name += fileHash;
|
|
113
|
+
|
|
114
|
+
obj.properties.push(t.ObjectProperty(
|
|
115
|
+
t.Identifier('name'),
|
|
116
|
+
t.StringLiteral(name)
|
|
117
|
+
));
|
|
118
|
+
|
|
119
|
+
return name;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// TODO: args should be a path instead of node
|
|
123
|
+
function findStubPropertyIndex(args) {
|
|
124
|
+
let obj = args[0];
|
|
125
|
+
let stubPropIndex = obj.properties.findIndex((property) => {
|
|
126
|
+
if (property.key.type !== 'Identifier') {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (property.key.name !== 'stub') {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return true;
|
|
135
|
+
});
|
|
136
|
+
let stub = obj.properties[stubPropIndex];
|
|
137
|
+
|
|
138
|
+
if (!stub) {
|
|
139
|
+
return -1;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (
|
|
143
|
+
stub.type === 'ObjectMethod' ||
|
|
144
|
+
stub.value.type === 'FunctionExpression' ||
|
|
145
|
+
stub.value.type === 'ArrowFunctionExpression'
|
|
146
|
+
) {
|
|
147
|
+
return stubPropIndex;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
if (stub.value.type !== 'BooleanLiteral') {
|
|
152
|
+
return -1;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (stub.value.value !== true) {
|
|
156
|
+
return -1;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// stub is set to true - use the run function
|
|
160
|
+
return obj.properties.findIndex((property) => {
|
|
161
|
+
if (property.key.type !== 'Identifier') {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (property.key.name !== 'run') {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return true;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
let canHaveMethods = false;
|
|
174
|
+
let canHavePublications = false;
|
|
175
|
+
let createMethodName = null;
|
|
176
|
+
let createPublicationName = null;
|
|
177
|
+
let methods = [];
|
|
178
|
+
let publications = [];
|
|
179
|
+
let isServer = false;
|
|
180
|
+
let filePath = ''
|
|
181
|
+
let imports = Object.create(null);
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
visitor: {
|
|
185
|
+
Program: {
|
|
186
|
+
enter(_, state) {
|
|
187
|
+
createMethodName = null;
|
|
188
|
+
createPublicationName = null;
|
|
189
|
+
methods = [];
|
|
190
|
+
publications = [];
|
|
191
|
+
|
|
192
|
+
let relPath = path
|
|
193
|
+
.relative(state.cwd, state.filename)
|
|
194
|
+
.split(path.sep)
|
|
195
|
+
.join(path.posix.sep);
|
|
196
|
+
filePath = relPath;
|
|
197
|
+
|
|
198
|
+
canHaveMethods = relPath.includes('/methods/') || relPath.startsWith('methods/');
|
|
199
|
+
canHavePublications = relPath.includes('/publications/') || relPath.startsWith('publications/');
|
|
200
|
+
|
|
201
|
+
isServer = caller.arch.startsWith('os.');
|
|
202
|
+
|
|
203
|
+
if (!canHaveMethods && !canHavePublications) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
exit(path) {
|
|
208
|
+
if (isServer || !canHaveMethods && !canHavePublications) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (methods.length === 0 && publications.length === 0) {
|
|
213
|
+
path.node.body = [];
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let body = [];
|
|
218
|
+
|
|
219
|
+
let importSpecifiers = [];
|
|
220
|
+
if (methods.length > 0) {
|
|
221
|
+
importSpecifiers.push(
|
|
222
|
+
t.ImportSpecifier(t.Identifier('_createClientMethod'), t.Identifier('_createClientMethod'))
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
if (publications.length > 0) {
|
|
226
|
+
importSpecifiers.push(
|
|
227
|
+
t.ImportSpecifier(t.Identifier('_createClientPublication'), t.Identifier('_createClientPublication'))
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
let importDecl = t.ImportDeclaration(
|
|
232
|
+
importSpecifiers,
|
|
233
|
+
t.StringLiteral('meteor/zodern:relay/client'),
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
body.push(importDecl);
|
|
237
|
+
|
|
238
|
+
methods.forEach(method => {
|
|
239
|
+
if (method.stub) {
|
|
240
|
+
let stubBody = method.stub.get('body').node ?
|
|
241
|
+
method.stub.get('body') : method.stub.get('value.body');
|
|
242
|
+
stubBody.traverse({
|
|
243
|
+
ReferencedIdentifier(subPath) {
|
|
244
|
+
if (stubBody.scope.hasOwnBinding(subPath.node.name)) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (subPath.node.name in imports) {
|
|
249
|
+
let importDesc = imports[subPath.node.name];
|
|
250
|
+
|
|
251
|
+
let specifier;
|
|
252
|
+
if (importDesc.type === 'ImportDefaultSpecifier') {
|
|
253
|
+
specifier = t.ImportDefaultSpecifier(t.Identifier(subPath.node.name));
|
|
254
|
+
} else if (importDesc.type === 'ImportNamespaceSpecifier') {
|
|
255
|
+
specifier = t.ImportNamespaceSpecifier(t.Identifier(subPath.node.name));
|
|
256
|
+
} else {
|
|
257
|
+
specifier = t.ImportSpecifier(
|
|
258
|
+
t.Identifier(importDesc.importName),
|
|
259
|
+
t.Identifier(subPath.node.name)
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// TODO: we should preserve the original order of the imports
|
|
264
|
+
body.push(t.ImportDeclaration(
|
|
265
|
+
[ specifier ],
|
|
266
|
+
t.StringLiteral(importDesc.source)
|
|
267
|
+
));
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
body.push(
|
|
274
|
+
createExport(method.export, '_createClientMethod', method.name, method.stub)
|
|
275
|
+
);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
publications.forEach(publication => {
|
|
279
|
+
body.push(
|
|
280
|
+
createExport(publication.export, '_createClientPublication', publication.name)
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
path.node.body = body;
|
|
285
|
+
|
|
286
|
+
return;
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
ImportDeclaration(path) {
|
|
290
|
+
path.node.specifiers.forEach(specifier => {
|
|
291
|
+
let type = specifier.type;
|
|
292
|
+
let hasImportName = type !== 'ImportDefaultSpecifier' &&
|
|
293
|
+
type !== 'ImportNamespaceSpecifier'
|
|
294
|
+
imports[specifier.local.name] = {
|
|
295
|
+
type,
|
|
296
|
+
importName: hasImportName ?
|
|
297
|
+
specifier.imported.name :
|
|
298
|
+
null,
|
|
299
|
+
source: path.node.source.value
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
if (!hasImportName) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (canHaveMethods && specifier.imported.name === 'createMethod') {
|
|
307
|
+
createMethodName = specifier.local.name;
|
|
308
|
+
}
|
|
309
|
+
if (canHavePublications && specifier.imported.name === 'createPublication') {
|
|
310
|
+
createPublicationName = specifier.local.name;
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
},
|
|
314
|
+
ExportDefaultDeclaration(path) {
|
|
315
|
+
if (path.node.declaration.type !== 'CallExpression') {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
let call = getFirstCallExpr(path.get('declaration'));
|
|
320
|
+
|
|
321
|
+
if (!call) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (
|
|
326
|
+
call.node.callee.name === createMethodName
|
|
327
|
+
) {
|
|
328
|
+
let name = getOrAddName(call.node.arguments, {
|
|
329
|
+
exportName: null,
|
|
330
|
+
filePath,
|
|
331
|
+
isPub: false
|
|
332
|
+
});
|
|
333
|
+
if (name === undefined) {
|
|
334
|
+
throw new Error('Unable to find name for createMethod');
|
|
335
|
+
}
|
|
336
|
+
let stubPropIndex = findStubPropertyIndex(call.node.arguments);
|
|
337
|
+
let stub;
|
|
338
|
+
if (stubPropIndex > -1) {
|
|
339
|
+
stub = call.get(`arguments.0.properties.${stubPropIndex}`);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
methods.push({
|
|
343
|
+
name: name,
|
|
344
|
+
export: null,
|
|
345
|
+
stub
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (
|
|
350
|
+
call.node.callee.name === createPublicationName
|
|
351
|
+
) {
|
|
352
|
+
let name = getOrAddName(call.node.arguments, {
|
|
353
|
+
exportName: null,
|
|
354
|
+
filePath,
|
|
355
|
+
isPub: true
|
|
356
|
+
});
|
|
357
|
+
if (name === undefined) {
|
|
358
|
+
throw new Error('Unable to find name for createMethod');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
publications.push({
|
|
362
|
+
name: name,
|
|
363
|
+
export: null
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
ExportNamedDeclaration(path) {
|
|
368
|
+
let declaration = path.get('declaration');
|
|
369
|
+
|
|
370
|
+
if (
|
|
371
|
+
// null when the code is something like "export { h };"
|
|
372
|
+
!declaration.node ||
|
|
373
|
+
declaration.isFunctionDeclaration()
|
|
374
|
+
) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (declaration.type == 'ClassDeclaration') {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (declaration.type !== 'VariableDeclaration') {
|
|
383
|
+
throw new Error(`export declarations of type ${declaration.type} are not supported`);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
declaration.get('declarations').forEach(vDeclaration => {
|
|
387
|
+
if (!vDeclaration.isVariableDeclarator()) {
|
|
388
|
+
throw new Error(`Unsupported declaration type in VariableDeclaration: ${vDeclaration.node.type}`);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (!vDeclaration.get('init').isCallExpression()) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
let call = getFirstCallExpr(vDeclaration.get('init'));
|
|
396
|
+
|
|
397
|
+
if (!call) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (
|
|
402
|
+
call.node.callee.name === createMethodName
|
|
403
|
+
) {
|
|
404
|
+
let name = getOrAddName(call.node.arguments, {
|
|
405
|
+
exportName: vDeclaration.node.id.name,
|
|
406
|
+
filePath,
|
|
407
|
+
isPub: false
|
|
408
|
+
});
|
|
409
|
+
if (name === undefined) {
|
|
410
|
+
throw new Error('Unable to find name for createMethod');
|
|
411
|
+
}
|
|
412
|
+
let stubPropIndex = findStubPropertyIndex(call.node.arguments);
|
|
413
|
+
let stub;
|
|
414
|
+
if (stubPropIndex > -1) {
|
|
415
|
+
stub = call.get(`arguments.0.properties.${stubPropIndex}`);
|
|
416
|
+
}
|
|
417
|
+
methods.push({
|
|
418
|
+
name: name,
|
|
419
|
+
export: vDeclaration.node.id.name,
|
|
420
|
+
stub
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (
|
|
425
|
+
call.node.callee.name === createPublicationName
|
|
426
|
+
) {
|
|
427
|
+
let name = getOrAddName(call.node.arguments, {
|
|
428
|
+
exportName: vDeclaration.node.id.name,
|
|
429
|
+
filePath,
|
|
430
|
+
isPub: true
|
|
431
|
+
});
|
|
432
|
+
if (name === undefined) {
|
|
433
|
+
throw new Error('Unable to find name for createMethod');
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
publications.push({
|
|
437
|
+
name: name,
|
|
438
|
+
export: vDeclaration.node.id.name
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
})
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
}
|