Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement bundling changes suggested in #4062 #4096

Open
wants to merge 13 commits into
base: 16.x.x
Choose a base branch
from
49 changes: 48 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -4,8 +4,55 @@
"description": "A Query Language and Runtime which can target any service.",
"license": "MIT",
"private": true,
"main": "index",
"main": "index.js",
"module": "index.mjs",
"exports": {
".": {
"types": {
"import": "./index.js.d.mts",
"default": "./index.d.ts"
},
"module": "./index.mjs",
"import": "./index.js.mjs",
"default": "./index.js"
},
yaacovCR marked this conversation as resolved.
Show resolved Hide resolved
"./execution/execute.js": {
"types": {
"import": "./execution/execute.js.d.mts",
"default": "./execution/execute.d.ts"
},
"module": "./execution/execute.mjs",
"import": "./execution/execute.js.mjs",
"default": "./execution/execute.js"
},
"./jsutils/instanceOf.js": {
"types": {
"import": "./jsutils/instanceOf.js.d.mts",
"default": "./jsutils/instanceOf.d.ts"
},
"module": "./jsutils/instanceOf.mjs",
"import": "./jsutils/instanceOf.js.mjs",
"default": "./jsutils/instanceOf.js"
},
"./language/parser.js": {
"types": {
"import": "./language/parser.js.d.mts",
"default": "./language/parser.d.ts"
},
"module": "./language/parser.mjs",
"import": "./language/parser.js.mjs",
"default": "./language/parser.js"
},
"./language/ast.js": {
"types": {
"import": "./language/ast.js.d.mts",
"default": "./language/ast.d.ts"
},
"module": "./language/ast.mjs",
"import": "./language/ast.js.mjs",
"default": "./language/ast.js"
}
},
"typesVersions": {
">=4.1.0": {
"*": [
147 changes: 147 additions & 0 deletions resources/build-npm.js
Original file line number Diff line number Diff line change
@@ -13,6 +13,14 @@ const {
showDirStats,
} = require('./utils.js');

const entryPoints = [
'index.ts',
'execution/execute.ts',
'jsutils/instanceOf.ts',
'language/parser.ts',
'language/ast.ts',
];
phryneas marked this conversation as resolved.
Show resolved Hide resolved

if (require.main === module) {
fs.rmSync('./npmDist', { recursive: true, force: true });
fs.mkdirSync('./npmDist');
@@ -57,11 +65,21 @@ if (require.main === module) {

const tsProgram = ts.createProgram(['src/index.ts'], tsOptions, tsHost);
const tsResult = tsProgram.emit();

assert(
!tsResult.emitSkipped,
'Fail to generate `*.d.ts` files, please run `npm run check`',
);

for (const [filename, contents] of Object.entries(
buildCjsEsmWrapper(
entryPoints.map((e) => './src/' + e),
tsProgram,
),
)) {
writeGeneratedFile(filename, contents);
}

assert(packageJSON.types === undefined, 'Unexpected "types" in package.json');
const supportedTSVersions = Object.keys(packageJSON.typesVersions);
assert(
@@ -107,6 +125,8 @@ function buildPackageJSON() {
delete packageJSON.scripts;
delete packageJSON.devDependencies;

packageJSON.type = 'commonjs';
yaacovCR marked this conversation as resolved.
Show resolved Hide resolved

// TODO: move to integration tests
const publishTag = packageJSON.publishConfig?.tag;
assert(publishTag != null, 'Should have packageJSON.publishConfig defined!');
@@ -137,3 +157,130 @@ function buildPackageJSON() {

return packageJSON;
}

/**
*
* @param {string[]} files
* @param {ts.Program} tsProgram
* @returns
*/
function buildCjsEsmWrapper(files, tsProgram) {
/**
* @type {Record<string, string>} inputFiles
*/
const inputFiles = {};
for (const file of files) {
const sourceFile = tsProgram.getSourceFile(file);
assert(sourceFile, `No source file found for ${file}`);

const generatedFileName = path.relative(
path.dirname(tsProgram.getRootFileNames()[0]),
file.replace(/\.ts$/, '.js.mts'),
);
const exportFrom = ts.factory.createStringLiteral(
'./' + path.basename(file, '.ts') + '.js',
);

/**
* @type {ts.Statement[]}
*/
const statements = [];

/** @type {string[]} */
const exports = [];

/** @type {string[]} */
const typeExports = [];

sourceFile.forEachChild((node) => {
if (ts.isExportDeclaration(node)) {
if (node.exportClause && ts.isNamedExports(node.exportClause)) {
for (const element of node.exportClause.elements) {
if (node.isTypeOnly || element.isTypeOnly) {
typeExports.push(element.name.text);
} else {
exports.push(element.name.text);
}
}
}
} else if (
node.modifiers?.some(
(modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword,
)
) {
if (ts.isVariableStatement(node)) {
for (const declaration of node.declarationList.declarations) {
if (declaration.name && ts.isIdentifier(declaration.name)) {
exports.push(declaration.name.text);
}
}
} else if (
ts.isFunctionDeclaration(node) ||
ts.isClassDeclaration(node)
) {
exports.push(node.name.text);
} else if (ts.isTypeAliasDeclaration(node)) {
typeExports.push(node.name.text);
}
}
});
if (exports.length > 0) {
statements.push(
ts.factory.createExportDeclaration(
undefined,
undefined,
false,
ts.factory.createNamedExports(
exports.map((name) =>
ts.factory.createExportSpecifier(false, undefined, name),
),
),
exportFrom,
),
);
}
if (typeExports.length > 0) {
statements.push(
ts.factory.createExportDeclaration(
undefined,
undefined,
true,
ts.factory.createNamedExports(
typeExports.map((name) =>
ts.factory.createExportSpecifier(false, undefined, name),
),
),
exportFrom,
),
);
}
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
inputFiles[generatedFileName] = printer.printFile(
ts.factory.createSourceFile(
statements,
ts.factory.createToken(ts.SyntaxKind.EndOfFileToken),
ts.NodeFlags.None,
),
);
}
/**
* @type {ts.CompilerOptions} options
*/
const options = {
...tsProgram.getCompilerOptions(),
declaration: true,
emitDeclarationOnly: false,
isolatedModules: true,
module: ts.ModuleKind.ESNext,
};
options.outDir = options.declarationDir;
const results = {};
const host = ts.createCompilerHost(options);
host.writeFile = (fileName, contents) => (results[fileName] = contents);
host.readFile = (fileName) => inputFiles[fileName];

const program = ts.createProgram(Object.keys(inputFiles), options, host);
program.emit();

return results;
}
11 changes: 11 additions & 0 deletions src/execution/execute.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
/**
* Quoted as "used by external libraries".
* Missing exports from `graphql`:
* * ExecutionContext
* * assertValidExecutionArguments @internal
* * buildExecutionContext @internal
* * buildResolveInfo @internal
* * getFieldDef @internal
* Should we still expose this file?
*/

import { devAssert } from '../jsutils/devAssert';
import { inspect } from '../jsutils/inspect';
import { invariant } from '../jsutils/invariant';
6 changes: 6 additions & 0 deletions src/execution/values.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/**
* Quoted as "used by external libraries".
* Missing exports from `graphql`: none
* Should we still expose this file?
*/

import { inspect } from '../jsutils/inspect';
import { keyMap } from '../jsutils/keyMap';
import type { Maybe } from '../jsutils/Maybe';
4 changes: 4 additions & 0 deletions src/jsutils/Maybe.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
/**
* Quoted as "used by external libraries".
* Should we still expose these?
*/
/** Conveniently represents flow's "Maybe" type https://flow.org/en/docs/types/maybe/ */
export type Maybe<T> = null | undefined | T;
6 changes: 6 additions & 0 deletions src/jsutils/ObjMap.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/**
* Quoted as "used by external libraries".
* All of these could be replaced by Record<string, T> or Readonly<Record<string, T>>
* Should we still expose these?
*/

export interface ObjMap<T> {
[key: string]: T;
}
4 changes: 4 additions & 0 deletions src/jsutils/PromiseOrValue.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
/**
* Quoted as "used by external libraries".
* Should we still expose these?
*/
export type PromiseOrValue<T> = Promise<T> | T;
7 changes: 7 additions & 0 deletions src/language/ast.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* Quoted as "used by external libraries".
* Missing exports from `graphql`:
* * isNode @internal
* * QueryDocumentKeys @internal
* Should we still expose this file?
*/
import type { Kind } from './kinds';
import type { Source } from './source';
import type { TokenKind } from './tokenKind';
7 changes: 7 additions & 0 deletions src/language/lexer.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* Quoted as "used by external libraries".
* Missing exports from `graphql`:
* * isPunctuatorTokenKind @internal
* Should we still expose this file?
*/

import { syntaxError } from '../error/syntaxError';

import { Token } from './ast';
7 changes: 7 additions & 0 deletions src/language/parser.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* Quoted as "used by external libraries".
* Missing exports from `graphql`:
* * Parser @internal
* Should we still expose this file?
*/

import type { Maybe } from '../jsutils/Maybe';

import type { GraphQLError } from '../error/GraphQLError';
7 changes: 7 additions & 0 deletions src/type/schema.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* Quoted as "used by external libraries".
* Missing exports from `graphql`:
* * GraphQLSchemaNormalizedConfig @internal
* * GraphQLSchemaValidationOptions
* Should we still expose this file?
*/
import { devAssert } from '../jsutils/devAssert';
import { inspect } from '../jsutils/inspect';
import { instanceOf } from '../jsutils/instanceOf';
10 changes: 10 additions & 0 deletions src/validation/ValidationContext.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
/**
* Quoted as "used by external libraries".
* Missing exports from `graphql`:
* * ASTValidationContext
* * ASTValidationRule
* * SDLValidationContext
* * SDLValidationRule
* Should we still expose this file?
*/

import type { Maybe } from '../jsutils/Maybe';
import type { ObjMap } from '../jsutils/ObjMap';

7 changes: 7 additions & 0 deletions src/validation/specifiedRules.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* Quoted as "used by external libraries".
* Missing exports from `graphql`:
* * specifiedSDLRules @internal
* Should we still expose this file?
*/

// Spec Section: "Executable Definitions"
import { ExecutableDefinitionsRule } from './rules/ExecutableDefinitionsRule';
// Spec Section: "Field Selections on Objects, Interfaces, and Unions Types"
9 changes: 9 additions & 0 deletions src/validation/validate.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
/**
* Quoted as "used by external libraries".
* Missing exports from `graphql`:
* * assertValidSDL @internal
* * assertValidSDLExtension @internal
* * validateSDL @internal
* Should we still expose this file?
*/

import { devAssert } from '../jsutils/devAssert';
import type { Maybe } from '../jsutils/Maybe';