@isograph/babel-plugin 0.2.0 → 0.3.1

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.
@@ -9,29 +9,40 @@ const configExplorer = cosmiconfig('isograph', {
9
9
  '.json': cosmiconfig.loadJson,
10
10
  },
11
11
  });
12
-
12
+ /** @type {NonNullable<import("cosmiconfig").CosmiconfigResult>} */
13
13
  let IsographConfig;
14
14
  const result = configExplorer.searchSync();
15
15
  if (result) {
16
- IsographConfig = result.config;
16
+ IsographConfig = result;
17
17
  } else {
18
18
  throw new Error(
19
19
  'No config found. Do you have a isograph.config.json file somewhere?',
20
20
  );
21
21
  }
22
22
 
23
+ /** @typedef {import("@babel/core")} babel*/
24
+
25
+ /**
26
+ * @typedef {Object} Context
27
+ * @property {typeof babel.types} [types]
28
+ * */
29
+
30
+ /**
31
+ * @param {Context} context
32
+ * @returns {babel.PluginObj} */
23
33
  module.exports = function BabelPluginIsograph(context) {
24
- const { types: t } = context;
25
- if (!t) {
34
+ const { types } = context;
35
+ if (!types) {
26
36
  throw new Error(
27
37
  'BabelPluginIsograph: Expected plugin context to include "types", but got:' +
28
38
  String(context),
29
39
  );
30
40
  }
31
41
 
42
+ /** @type {babel.Visitor<babel.PluginPass>} */
32
43
  const visitor = {
33
44
  CallExpression(path) {
34
- compileTag(t, path, IsographConfig);
45
+ compileTag(types, path, IsographConfig);
35
46
  },
36
47
  };
37
48
 
package/compileTag.js CHANGED
@@ -1,7 +1,15 @@
1
1
  'use strict';
2
2
 
3
+ const { addDefault } = require('@babel/helper-module-imports');
3
4
  const pathModule = require('path');
5
+ const os = require('os');
4
6
 
7
+ /**
8
+ * @typedef {import("@babel/core")} babel
9
+ * @param {typeof babel.types} t
10
+ * @param {babel.NodePath<babel.types.CallExpression>} path
11
+ * @param {NonNullable<import("cosmiconfig").CosmiconfigResult>} config
12
+ */
5
13
  function compileTag(t, path, config) {
6
14
  const callee = path.node.callee;
7
15
  if (t.isIdentifier(callee) && callee.name === 'iso' && path.node.arguments) {
@@ -10,8 +18,20 @@ function compileTag(t, path, config) {
10
18
  // This throws if the tag is invalid
11
19
  compileImportStatement(t, path, type, field, 'entrypoint', config);
12
20
  } else if (keyword === 'field') {
13
- // No-op
14
- return false;
21
+ if (t.isCallExpression(path.parentPath.node)) {
22
+ const firstArg = path.parentPath.node.arguments[0];
23
+ if (path.parentPath.node.arguments.length === 1 && firstArg != null) {
24
+ path.parentPath.replaceWith(firstArg);
25
+ } else {
26
+ throw new Error(
27
+ 'Invalid iso tag usage. The iso function should be passed at most one argument.',
28
+ );
29
+ }
30
+ } else {
31
+ path.replaceWith(
32
+ t.arrowFunctionExpression([t.identifier('x')], t.identifier('x')),
33
+ );
34
+ }
15
35
  } else {
16
36
  throw new Error(
17
37
  "Invalid iso tag usage. Expected 'entrypoint' or 'field'.",
@@ -26,25 +46,37 @@ const typeAndFieldRegex = new RegExp(
26
46
  'm',
27
47
  );
28
48
 
49
+ /**
50
+ * @param {babel.NodePath<babel.types.CallExpression>} path
51
+ * */
29
52
  function getTypeAndField(path) {
30
- if (path.node.arguments.length !== 1) {
53
+ const firstArg = path.node.arguments[0];
54
+ if (path.node.arguments.length !== 1 || firstArg == null) {
31
55
  throw new Error(
32
56
  `BabelPluginIsograph: Iso invocation require one parameter, found ${path.node.arguments.length}`,
33
57
  );
34
58
  }
35
- const quasis = path.node.arguments[0].quasis;
36
- if (quasis.length !== 1) {
59
+
60
+ if (firstArg.type !== 'TemplateLiteral') {
61
+ throw new Error(
62
+ 'BabelPluginIsograph: Only template literals are allowed in iso fragments.',
63
+ );
64
+ }
65
+
66
+ const quasis = firstArg.quasis;
67
+ const firstQuasi = quasis[0];
68
+ if (quasis.length !== 1 || firstQuasi == null) {
37
69
  throw new Error(
38
70
  'BabelPluginIsograph: Substitutions are not allowed in iso fragments.',
39
71
  );
40
72
  }
41
73
 
42
- const content = quasis[0].value.raw;
74
+ const content = firstQuasi.value.raw;
43
75
  const typeAndField = typeAndFieldRegex.exec(content);
44
76
 
45
- const keyword = typeAndField[1];
46
- const type = typeAndField[2];
47
- const field = typeAndField[3];
77
+ const keyword = typeAndField?.[1];
78
+ const type = typeAndField?.[2];
79
+ const field = typeAndField?.[3];
48
80
 
49
81
  if (keyword == null || type == null || field == null) {
50
82
  throw new Error(
@@ -54,14 +86,23 @@ function getTypeAndField(path) {
54
86
  return { keyword, type, field };
55
87
  }
56
88
 
89
+ /**
90
+ * @param {typeof babel.types} t
91
+ * @param {babel.NodePath<babel.types.CallExpression>} path
92
+ * @param {string} type
93
+ * @param {string} field
94
+ * @param {string} artifactType
95
+ * @param {NonNullable<import("cosmiconfig").CosmiconfigResult>} config
96
+ */
57
97
  function compileImportStatement(t, path, type, field, artifactType, config) {
58
98
  const filename = path.state.filename;
59
99
  const folder = pathModule.dirname(filename);
60
- const cwd = path.state.cwd;
100
+ const cwd = pathModule.dirname(config.filepath);
61
101
  const artifactDirectory = pathModule.join(
62
102
  cwd,
63
- config.artifact_directory ?? config.project_root,
103
+ config.config['artifact_directory'] ?? config.config['project_root'],
64
104
  );
105
+ const module = config.config['options']?.['module'];
65
106
 
66
107
  const fileToArtifactDir = pathModule.relative(folder, artifactDirectory);
67
108
  const artifactDirToArtifact = `/__isograph/${type}/${field}/${artifactType}.ts`;
@@ -70,6 +111,10 @@ function compileImportStatement(t, path, type, field, artifactType, config) {
70
111
  artifactDirToArtifact,
71
112
  );
72
113
 
114
+ if (os.platform() === 'win32') {
115
+ fileToArtifact = fileToArtifact.replace(/\\/g, '/');
116
+ }
117
+
73
118
  // If we do not have to traverse upward, e.g. if the resolver is in
74
119
  // src/HomePage, and the artifact directory is src/, then fileToArtifact
75
120
  // will start with a /. require('/...') is not good, as that is treated
@@ -78,14 +123,29 @@ function compileImportStatement(t, path, type, field, artifactType, config) {
78
123
  fileToArtifact = '.' + fileToArtifact;
79
124
  }
80
125
 
81
- path.replaceWith(
82
- t.memberExpression(
83
- t.CallExpression(t.Identifier('require'), [
84
- t.StringLiteral(fileToArtifact),
85
- ]),
86
- t.Identifier('default'),
87
- ),
88
- );
126
+ if (module === 'esmodule') {
127
+ const program = path.scope.getProgramParent();
128
+ const imports = /** @type {Map<string, string>} */ (
129
+ program.data['imports'] ??= new Map()
130
+ );
131
+
132
+ let id = imports.get(fileToArtifact);
133
+ if (id == null) {
134
+ id = addDefault(path, fileToArtifact, { nameHint: field }).name;
135
+ imports.set(fileToArtifact, id);
136
+ }
137
+
138
+ path.replaceWith(t.identifier(id));
139
+ } else {
140
+ path.replaceWith(
141
+ t.memberExpression(
142
+ t.callExpression(t.identifier('require'), [
143
+ t.stringLiteral(fileToArtifact),
144
+ ]),
145
+ t.identifier('default'),
146
+ ),
147
+ );
148
+ }
89
149
  }
90
150
 
91
151
  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.2.0",
5
+ "version": "0.3.1",
6
6
  "keywords": [
7
7
  "graphql",
8
8
  "isograph",
@@ -15,13 +15,22 @@
15
15
  "url": "git+https://github.com/isographlabs/isograph.git",
16
16
  "directory": "libs/isograph-babel-plugin"
17
17
  },
18
+ "scripts": {
19
+ "tsc": "tsc",
20
+ "tsc-force": "tsc --build --clean && tsc --build --force"
21
+ },
18
22
  "dependencies": {
23
+ "@babel/helper-module-imports": "^7.0.0",
19
24
  "babel-plugin-macros": "^2.0.0",
20
25
  "cosmiconfig": "^5.0.5",
21
26
  "graphql": "15.3.0"
22
27
  },
23
28
  "devDependencies": {
24
29
  "@babel/core": "^7.20.0",
30
+ "@babel/types": "^7.25.6",
31
+ "@types/babel__core": "^7.20.5",
32
+ "@types/babel__helper-module-imports": "^7.18.3",
33
+ "@types/cosmiconfig": "^5.0.3",
25
34
  "prettier": "2.8.8",
26
35
  "prettier-plugin-hermes-parser": "0.16.0"
27
36
  },
package/stub.ts ADDED
@@ -0,0 +1,7 @@
1
+ import type cosmiconfig from 'cosmiconfig';
2
+
3
+ declare module 'cosmiconfig' {
4
+ export const loadJson: cosmiconfig.LoaderEntry;
5
+ }
6
+
7
+ export {};
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.build.json",
3
+ "compilerOptions": {
4
+ "allowJs": true,
5
+ "checkJs": true,
6
+ "noEmit": true
7
+ },
8
+ "include": ["./**/*.ts", "./**/*.js"]
9
+ }