@graphox/babel-plugin
Advanced tools
+61
-16
@@ -238,2 +238,13 @@ const path = require('path'); | ||
| importPath.get('specifiers').forEach((specifier) => { | ||
| if (specifier.isImportDefaultSpecifier() || specifier.isImportNamespaceSpecifier()) { | ||
| if (!importIsTypeOnly) { | ||
| const importKind = specifier.isImportDefaultSpecifier() ? 'default' : 'namespace'; | ||
| throw specifier.buildCodeFrameError( | ||
| `@graphox/babel-plugin could not fully rewrite this ${importKind} import from "${src}". ` + | ||
| 'Only named document imports and graphql/gql are supported.', | ||
| ); | ||
| } | ||
| return; | ||
| } | ||
| if (specifier.isImportSpecifier()) { | ||
@@ -278,2 +289,7 @@ // Handle both Identifier and StringLiteral for imported name | ||
| } | ||
| } else if (!specifierIsTypeOnly) { | ||
| throw specifier.buildCodeFrameError( | ||
| `@graphox/babel-plugin could not rewrite "${importedName}" from "${src}". ` + | ||
| 'Run Graphox codegen and ensure the manifest includes this document.', | ||
| ); | ||
| } | ||
@@ -304,20 +320,30 @@ } | ||
| if (source) { | ||
| const normalizedSource = normalize(source); | ||
| const entry = manifest.get(normalizedSource); | ||
| if (entry) { | ||
| const codegenAbsPath = path.join(absoluteOutputDir, entry.path); | ||
| let relPath = path.relative(path.dirname(currentFile), codegenAbsPath); | ||
| relPath = toPosixPath(relPath); | ||
| if (!relPath.startsWith('.') && !path.isAbsolute(relPath)) { | ||
| relPath = './' + relPath; | ||
| } | ||
| // Append the emit extension | ||
| relPath += extension; | ||
| if (!source) { | ||
| throw callPath.buildCodeFrameError( | ||
| `@graphox/babel-plugin could not statically analyze this ${callee.node.name}() call. ` + | ||
| 'Use a single static string/template literal so it can be resolved from the manifest.', | ||
| ); | ||
| } | ||
| const uniqueLocalName = getLocalName(entry.name, callPath.scope); | ||
| newImports.set(uniqueLocalName, { sourcePath: relPath, importedName: entry.name }); | ||
| callPath.replaceWith(t.identifier(uniqueLocalName)); | ||
| } | ||
| const normalizedSource = normalize(source); | ||
| const entry = manifest.get(normalizedSource); | ||
| if (!entry) { | ||
| throw callPath.buildCodeFrameError( | ||
| `@graphox/babel-plugin could not find this ${callee.node.name}() document in the manifest. ` + | ||
| 'Run Graphox codegen and ensure the build is using the correct manifest.', | ||
| ); | ||
| } | ||
| const codegenAbsPath = path.join(absoluteOutputDir, entry.path); | ||
| let relPath = path.relative(path.dirname(currentFile), codegenAbsPath); | ||
| relPath = toPosixPath(relPath); | ||
| if (!relPath.startsWith('.') && !path.isAbsolute(relPath)) { | ||
| relPath = './' + relPath; | ||
| } | ||
| // Append the emit extension | ||
| relPath += extension; | ||
| const uniqueLocalName = getLocalName(entry.name, callPath.scope); | ||
| newImports.set(uniqueLocalName, { sourcePath: relPath, importedName: entry.name }); | ||
| callPath.replaceWith(t.identifier(uniqueLocalName)); | ||
| } | ||
@@ -328,2 +354,21 @@ } | ||
| // Ensure we never remove the entrypoint import while runtime references still exist. | ||
| programPath.traverse({ | ||
| Identifier(idPath) { | ||
| if (!idPath.isReferencedIdentifier()) { | ||
| return; | ||
| } | ||
| const binding = idPath.scope.getBinding(idPath.node.name); | ||
| if (!binding || !graphqlIds.has(binding)) { | ||
| return; | ||
| } | ||
| throw idPath.buildCodeFrameError( | ||
| `@graphox/babel-plugin left a runtime reference to "${idPath.node.name}" after rewriting. ` + | ||
| 'All Graphox graphql/gql imports must be fully inlined before the import is removed.', | ||
| ); | ||
| }, | ||
| }); | ||
| // Third pass: remove ALL imports from graphql.ts/index.ts | ||
@@ -330,0 +375,0 @@ programPath.traverse({ |
+48
-20
@@ -67,12 +67,9 @@ import { describe, it, expect } from 'vitest'; | ||
| it('removes all imports from graphql.ts including unknown specifiers', () => { | ||
| const code = "import { graphql, other } from './gen/graphql'; const q = graphql(`query { me { id } }`);"; | ||
| const output = transform(code, defaultOptions); | ||
| it('throws when a value import from graphql.ts cannot be rewritten', () => { | ||
| const code = "import { graphql, other } from './gen/graphql'; const q = graphql(`query { me { id } }`); console.log(other);"; | ||
| expect(output).toContain('import { MyQueryDocument } from "./gen/query.codegen";'); | ||
| expect(output).not.toContain('from "./gen/graphql"'); | ||
| expect(output).not.toContain('graphql,'); | ||
| expect(output).not.toContain(', graphql'); | ||
| expect(output).not.toContain('other'); | ||
| }); | ||
| expect(() => transform(code, defaultOptions)).toThrow( | ||
| /could not rewrite "other" from "\.\/gen\/graphql"/, | ||
| ); | ||
| }); | ||
@@ -109,11 +106,43 @@ it('resolves relative paths correctly', () => { | ||
| it('supports gql tag', () => { | ||
| const code = "import { gql } from './gen/graphql'; const q = gql(`query { me { id } }`);"; | ||
| const output = transform(code, defaultOptions); | ||
| it('supports gql tag', () => { | ||
| const code = "import { gql } from './gen/graphql'; const q = gql(`query { me { id } }`);"; | ||
| const output = transform(code, defaultOptions); | ||
| expect(output).toContain('import { MyQueryDocument } from "./gen/query.codegen";'); | ||
| expect(output).toContain('const q = MyQueryDocument;'); | ||
| expect(output).not.toContain('from "./gen/graphql"'); | ||
| }); | ||
| expect(output).toContain('const q = MyQueryDocument;'); | ||
| expect(output).not.toContain('from "./gen/graphql"'); | ||
| }); | ||
| it('throws when graphql() is missing from the manifest', () => { | ||
| const code = "import { graphql } from './gen/graphql'; const q = graphql(`query Missing { me { id } }`);"; | ||
| expect(() => transform(code, defaultOptions)).toThrow( | ||
| /could not find this graphql\(\) document in the manifest/i, | ||
| ); | ||
| }); | ||
| it('throws when graphql() is not a single static string', () => { | ||
| const code = ` | ||
| import { graphql } from './gen/graphql'; | ||
| const query = 'query { me { id } }'; | ||
| const q = graphql(query); | ||
| `; | ||
| expect(() => transform(code, defaultOptions)).toThrow( | ||
| /could not statically analyze this graphql\(\) call/i, | ||
| ); | ||
| }); | ||
| it('throws when a graphql import is still referenced after rewriting', () => { | ||
| const code = ` | ||
| import { graphql } from './gen/graphql'; | ||
| const tag = graphql; | ||
| console.log(tag); | ||
| `; | ||
| expect(() => transform(code, defaultOptions)).toThrow( | ||
| /left a runtime reference to "graphql" after rewriting/i, | ||
| ); | ||
| }); | ||
| it('is whitespace insensitive (normalization)', () => { | ||
@@ -281,9 +310,8 @@ const code = ` | ||
| it('removes non-document imports while rewriting document imports', () => { | ||
| it('throws on non-document imports alongside rewritten document imports', () => { | ||
| const code = "import { GetUserDocument, SomeOtherType } from './gen/graphql';"; | ||
| const output = transform(code, reExportOptions); | ||
| expect(output).toContain("import { GetUserDocument } from \"./gen/user.codegen\";"); | ||
| expect(output).not.toContain("from './gen/graphql'"); | ||
| expect(output).not.toContain('SomeOtherType'); | ||
| expect(() => transform(code, reExportOptions)).toThrow( | ||
| /could not rewrite "SomeOtherType" from "\.\/gen\/graphql"/, | ||
| ); | ||
| }); | ||
@@ -290,0 +318,0 @@ |
+1
-1
| { | ||
| "name": "@graphox/babel-plugin", | ||
| "version": "0.4.0", | ||
| "version": "0.4.1", | ||
| "description": "Babel plugin for Graphox codesplitting", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
56633
5.62%1215
5.29%