@isograph/babel-plugin 0.3.0 → 0.4.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/BabelPluginIsograph.test.js +97 -0
- package/compileTag.js +67 -31
- package/package.json +6 -2
- package/stub.ts +1 -0
- package/tsconfig.json +1 -2
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { transform } from '@babel/core';
|
|
2
|
+
import { describe, expect, test, vi } from 'vitest';
|
|
3
|
+
import plugin from './BabelPluginIsograph';
|
|
4
|
+
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
async function mock(mockedUri, stub) {
|
|
7
|
+
const { Module } = await import('module');
|
|
8
|
+
const path = await import('path');
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
Module._load_original = Module._load;
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
Module._load = (uri, parent) => {
|
|
13
|
+
if (uri === mockedUri) return stub(path);
|
|
14
|
+
// @ts-ignore
|
|
15
|
+
return Module._load_original(uri, parent);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// In order to test `require`
|
|
20
|
+
vi.hoisted(
|
|
21
|
+
() =>
|
|
22
|
+
// @ts-ignore
|
|
23
|
+
void mock('cosmiconfig', (path) => () => ({
|
|
24
|
+
searchSync: () => ({
|
|
25
|
+
config: {
|
|
26
|
+
project_root: './src/components',
|
|
27
|
+
schema: './backend/schema.graphql',
|
|
28
|
+
schema_extensions: ['./backend/schema-extension.graphql'],
|
|
29
|
+
options: {
|
|
30
|
+
module: 'esmodule',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
filepath: `${path.resolve('.')}/isograph.config.json`,
|
|
34
|
+
}),
|
|
35
|
+
})),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
describe('Babel plugin Isograph', () => {
|
|
39
|
+
const transformerOpts = {
|
|
40
|
+
babelrc: false,
|
|
41
|
+
filename: './src/components/Home/Header/File.ts',
|
|
42
|
+
plugins: [[plugin, {}]],
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
test('should return an identity for non called iso function', () => {
|
|
46
|
+
const code = `
|
|
47
|
+
export const HomeRoute = iso(\`
|
|
48
|
+
field Query.HomeRoute @component {
|
|
49
|
+
pets {
|
|
50
|
+
id
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
\`);
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
const result = transform(code, transformerOpts) ?? { code: '' };
|
|
57
|
+
|
|
58
|
+
expect(result.code).toMatchInlineSnapshot(
|
|
59
|
+
`"export const HomeRoute = x => x;"`,
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('should preserve function call when iso applied', () => {
|
|
64
|
+
const code = `
|
|
65
|
+
export const HomeRoute = iso(\`
|
|
66
|
+
field Query.HomeRoute @component {
|
|
67
|
+
pets {
|
|
68
|
+
id
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
\`)(function HomeRouteComponent() {
|
|
72
|
+
return 'Render';
|
|
73
|
+
});
|
|
74
|
+
`;
|
|
75
|
+
|
|
76
|
+
const result = transform(code, transformerOpts) ?? { code: '' };
|
|
77
|
+
|
|
78
|
+
expect(result.code).toMatchInlineSnapshot(`
|
|
79
|
+
"export const HomeRoute = function HomeRouteComponent() {
|
|
80
|
+
return 'Render';
|
|
81
|
+
};"
|
|
82
|
+
`);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('should transform the iso function to a require call', () => {
|
|
86
|
+
const code = `function test() { const a=iso(\`entrypoint Query.HomeRoute\`); }`;
|
|
87
|
+
|
|
88
|
+
const result = transform(code, transformerOpts) ?? { code: '' };
|
|
89
|
+
|
|
90
|
+
expect(result.code).toMatchInlineSnapshot(`
|
|
91
|
+
"import _HomeRoute from "../../__isograph/Query/HomeRoute/entrypoint.ts";
|
|
92
|
+
function test() {
|
|
93
|
+
const a = _HomeRoute;
|
|
94
|
+
}"
|
|
95
|
+
`);
|
|
96
|
+
});
|
|
97
|
+
});
|
package/compileTag.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { addDefault } = require('@babel/helper-module-imports');
|
|
3
4
|
const pathModule = require('path');
|
|
4
5
|
const os = require('os');
|
|
5
6
|
|
|
@@ -12,16 +13,28 @@ const os = require('os');
|
|
|
12
13
|
function compileTag(t, path, config) {
|
|
13
14
|
const callee = path.node.callee;
|
|
14
15
|
if (t.isIdentifier(callee) && callee.name === 'iso' && path.node.arguments) {
|
|
15
|
-
const { keyword,
|
|
16
|
+
const { keyword, parentObjectEntityName, selectableName } =
|
|
17
|
+
getParentObjectEntityNameAndSelectableName(path);
|
|
16
18
|
if (keyword === 'entrypoint') {
|
|
17
19
|
// This throws if the tag is invalid
|
|
18
|
-
compileImportStatement(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
compileImportStatement(
|
|
21
|
+
t,
|
|
22
|
+
path,
|
|
23
|
+
parentObjectEntityName,
|
|
24
|
+
selectableName,
|
|
25
|
+
'entrypoint',
|
|
26
|
+
config,
|
|
27
|
+
);
|
|
28
|
+
} else if (keyword === 'field' || keyword === 'pointer') {
|
|
29
|
+
if (t.isCallExpression(path.parentPath.node)) {
|
|
30
|
+
const firstArg = path.parentPath.node.arguments[0];
|
|
31
|
+
if (path.parentPath.node.arguments.length === 1 && firstArg != null) {
|
|
32
|
+
path.parentPath.replaceWith(firstArg);
|
|
33
|
+
} else {
|
|
34
|
+
throw new Error(
|
|
35
|
+
'Invalid iso tag usage. The iso function should be passed at most one argument.',
|
|
36
|
+
);
|
|
37
|
+
}
|
|
25
38
|
} else {
|
|
26
39
|
path.replaceWith(
|
|
27
40
|
t.arrowFunctionExpression([t.identifier('x')], t.identifier('x')),
|
|
@@ -29,54 +42,61 @@ function compileTag(t, path, config) {
|
|
|
29
42
|
}
|
|
30
43
|
} else {
|
|
31
44
|
throw new Error(
|
|
32
|
-
"Invalid iso tag usage. Expected 'entrypoint' or '
|
|
45
|
+
"Invalid iso tag usage. Expected 'entrypoint', 'field' or 'pointer'.",
|
|
33
46
|
);
|
|
34
47
|
}
|
|
35
48
|
}
|
|
36
49
|
return false;
|
|
37
50
|
}
|
|
38
51
|
|
|
39
|
-
const
|
|
40
|
-
'\\s*(entrypoint|field)\\s*([^\\.\\s]+)\\.([^\\s\\(]+)',
|
|
52
|
+
const parentObjectEntityNameAndSelectableNameRegex = new RegExp(
|
|
53
|
+
'\\s*(entrypoint|field|pointer)\\s*([^\\.\\s]+)\\.([^\\s\\(]+)',
|
|
41
54
|
'm',
|
|
42
55
|
);
|
|
43
56
|
|
|
44
57
|
/**
|
|
45
58
|
* @param {babel.NodePath<babel.types.CallExpression>} path
|
|
46
|
-
|
|
47
|
-
function
|
|
48
|
-
|
|
59
|
+
**/
|
|
60
|
+
function getParentObjectEntityNameAndSelectableName(path) {
|
|
61
|
+
const firstArg = path.node.arguments[0];
|
|
62
|
+
if (path.node.arguments.length !== 1 || firstArg == null) {
|
|
49
63
|
throw new Error(
|
|
50
64
|
`BabelPluginIsograph: Iso invocation require one parameter, found ${path.node.arguments.length}`,
|
|
51
65
|
);
|
|
52
66
|
}
|
|
53
67
|
|
|
54
|
-
if (
|
|
68
|
+
if (firstArg.type !== 'TemplateLiteral') {
|
|
55
69
|
throw new Error(
|
|
56
70
|
'BabelPluginIsograph: Only template literals are allowed in iso fragments.',
|
|
57
71
|
);
|
|
58
72
|
}
|
|
59
73
|
|
|
60
|
-
const quasis =
|
|
61
|
-
|
|
74
|
+
const quasis = firstArg.quasis;
|
|
75
|
+
const firstQuasi = quasis[0];
|
|
76
|
+
if (quasis.length !== 1 || firstQuasi == null) {
|
|
62
77
|
throw new Error(
|
|
63
78
|
'BabelPluginIsograph: Substitutions are not allowed in iso fragments.',
|
|
64
79
|
);
|
|
65
80
|
}
|
|
66
81
|
|
|
67
|
-
const content =
|
|
68
|
-
const typeAndField =
|
|
82
|
+
const content = firstQuasi.value.raw;
|
|
83
|
+
const typeAndField =
|
|
84
|
+
parentObjectEntityNameAndSelectableNameRegex.exec(content);
|
|
69
85
|
|
|
70
86
|
const keyword = typeAndField?.[1];
|
|
71
|
-
const
|
|
72
|
-
const
|
|
87
|
+
const parentObjectEntityName = typeAndField?.[2];
|
|
88
|
+
const selectableName = typeAndField?.[3];
|
|
73
89
|
|
|
74
|
-
if (
|
|
90
|
+
if (
|
|
91
|
+
keyword == null ||
|
|
92
|
+
parentObjectEntityName == null ||
|
|
93
|
+
selectableName == null
|
|
94
|
+
) {
|
|
75
95
|
throw new Error(
|
|
76
96
|
'Malformed iso literal. I hope the iso compiler failed to accept this literal!',
|
|
77
97
|
);
|
|
78
98
|
}
|
|
79
|
-
return { keyword,
|
|
99
|
+
return { keyword, parentObjectEntityName, selectableName };
|
|
80
100
|
}
|
|
81
101
|
|
|
82
102
|
/**
|
|
@@ -95,6 +115,7 @@ function compileImportStatement(t, path, type, field, artifactType, config) {
|
|
|
95
115
|
cwd,
|
|
96
116
|
config.config['artifact_directory'] ?? config.config['project_root'],
|
|
97
117
|
);
|
|
118
|
+
const module = config.config['options']?.['module'];
|
|
98
119
|
|
|
99
120
|
const fileToArtifactDir = pathModule.relative(folder, artifactDirectory);
|
|
100
121
|
const artifactDirToArtifact = `/__isograph/${type}/${field}/${artifactType}.ts`;
|
|
@@ -115,14 +136,29 @@ function compileImportStatement(t, path, type, field, artifactType, config) {
|
|
|
115
136
|
fileToArtifact = '.' + fileToArtifact;
|
|
116
137
|
}
|
|
117
138
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
)
|
|
125
|
-
|
|
139
|
+
if (module === 'esmodule') {
|
|
140
|
+
const program = path.scope.getProgramParent();
|
|
141
|
+
const imports = /** @type {Map<string, string>} */ (
|
|
142
|
+
program.data['imports'] ??= new Map()
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
let id = imports.get(fileToArtifact);
|
|
146
|
+
if (id == null) {
|
|
147
|
+
id = addDefault(path, fileToArtifact, { nameHint: field }).name;
|
|
148
|
+
imports.set(fileToArtifact, id);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
path.replaceWith(t.identifier(id));
|
|
152
|
+
} else {
|
|
153
|
+
path.replaceWith(
|
|
154
|
+
t.memberExpression(
|
|
155
|
+
t.callExpression(t.identifier('require'), [
|
|
156
|
+
t.stringLiteral(fileToArtifact),
|
|
157
|
+
]),
|
|
158
|
+
t.identifier('default'),
|
|
159
|
+
),
|
|
160
|
+
);
|
|
161
|
+
}
|
|
126
162
|
}
|
|
127
163
|
|
|
128
164
|
module.exports = compileTag;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@isograph/babel-plugin",
|
|
3
3
|
"description": "A Babel plugin for use with Isograph applications.",
|
|
4
4
|
"homepage": "https://isograph.dev",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.4.0",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"graphql",
|
|
8
8
|
"isograph",
|
|
@@ -16,9 +16,12 @@
|
|
|
16
16
|
"directory": "libs/isograph-babel-plugin"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
|
-
"tsc": "tsc"
|
|
19
|
+
"tsc": "tsc",
|
|
20
|
+
"tsc-force": "tsc --build --clean && tsc --build --force",
|
|
21
|
+
"test": "vitest run"
|
|
20
22
|
},
|
|
21
23
|
"dependencies": {
|
|
24
|
+
"@babel/helper-module-imports": "^7.0.0",
|
|
22
25
|
"babel-plugin-macros": "^2.0.0",
|
|
23
26
|
"cosmiconfig": "^5.0.5",
|
|
24
27
|
"graphql": "15.3.0"
|
|
@@ -27,6 +30,7 @@
|
|
|
27
30
|
"@babel/core": "^7.20.0",
|
|
28
31
|
"@babel/types": "^7.25.6",
|
|
29
32
|
"@types/babel__core": "^7.20.5",
|
|
33
|
+
"@types/babel__helper-module-imports": "^7.18.3",
|
|
30
34
|
"@types/cosmiconfig": "^5.0.3",
|
|
31
35
|
"prettier": "2.8.8",
|
|
32
36
|
"prettier-plugin-hermes-parser": "0.16.0"
|
package/stub.ts
CHANGED