From 18f15ce6c9c626ef74c445a19c888ccebb2fe0c3 Mon Sep 17 00:00:00 2001 From: PabloSzx Date: Wed, 24 Nov 2021 15:56:17 -0300 Subject: [PATCH] npm esm tag --- .eslintignore | 1 + .eslintrc.yml | 4 + .gitignore | 1 + .prettierignore | 1 + integrationTests/node/{index.js => index.mjs} | 10 +-- integrationTests/node/package.json | 3 +- integrationTests/node/test.js | 7 +- integrationTests/ts/esm.ts | 38 +++++++++ integrationTests/ts/package.json | 1 + integrationTests/webpack/entry-esm.mjs | 13 +++ integrationTests/webpack/package.json | 1 + integrationTests/webpack/test.js | 13 ++- integrationTests/webpack/webpack.config.json | 7 +- resources/build-npm.ts | 79 +++++++++++++------ resources/integration-test.ts | 7 ++ resources/utils.ts | 6 +- 16 files changed, 154 insertions(+), 38 deletions(-) rename integrationTests/node/{index.js => index.mjs} (62%) create mode 100644 integrationTests/ts/esm.ts create mode 100644 integrationTests/webpack/entry-esm.mjs diff --git a/.eslintignore b/.eslintignore index 8700905993..082a0a423a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,6 +5,7 @@ /node_modules /reports /npmDist +/npmEsmDist /denoDist /websiteDist diff --git a/.eslintrc.yml b/.eslintrc.yml index 0b52540c40..6efcceb8f9 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -473,6 +473,10 @@ rules: yield-star-spacing: off overrides: + - files: + - 'integrationTests/node-esm/**/*.js' + parserOptions: + sourceType: module - files: '**/*.ts' parser: '@typescript-eslint/parser' parserOptions: diff --git a/.gitignore b/.gitignore index e3f24a8518..b3a652de18 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,6 @@ /node_modules /reports /npmDist +/npmEsmDist /denoDist /websiteDist diff --git a/.prettierignore b/.prettierignore index 64479f1f37..726959a3f2 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,5 +6,6 @@ /node_modules /reports /npmDist +/npmEsmDist /denoDist /websiteDist diff --git a/integrationTests/node/index.js b/integrationTests/node/index.mjs similarity index 62% rename from integrationTests/node/index.js rename to integrationTests/node/index.mjs index 3b39900906..6cd600729c 100644 --- a/integrationTests/node/index.js +++ b/integrationTests/node/index.mjs @@ -2,13 +2,13 @@ import assert from 'assert'; import { readFileSync } from 'fs'; -import { graphqlSync } from 'graphql'; -import { buildSchema } from 'graphql/utilities'; -import { version } from 'graphql/version'; +import { graphqlSync } from 'graphql-esm'; +import { buildSchema } from 'graphql-esm/utilities'; +import { version } from 'graphql-esm/version'; assert.deepStrictEqual( - version, - JSON.parse(readFileSync('./node_modules/graphql/package.json')).version, + version + '+esm', + JSON.parse(readFileSync('./node_modules/graphql-esm/package.json')).version, ); const schema = buildSchema('type Query { hello: String }'); diff --git a/integrationTests/node/package.json b/integrationTests/node/package.json index 2d2665ed9a..8271998588 100644 --- a/integrationTests/node/package.json +++ b/integrationTests/node/package.json @@ -6,6 +6,7 @@ "test": "node test.js" }, "dependencies": { - "graphql": "file:../graphql.tgz" + "graphql": "file:../graphql.tgz", + "graphql-esm": "file:../graphql-esm.tgz" } } diff --git a/integrationTests/node/test.js b/integrationTests/node/test.js index e3e55bb745..f47c48de89 100644 --- a/integrationTests/node/test.js +++ b/integrationTests/node/test.js @@ -14,7 +14,12 @@ for (const version of nodeVersions) { console.log(`Testing on node@${version} ...`); childProcess.execSync( - `docker run --rm --volume "$PWD":/usr/src/app -w /usr/src/app node:${version}-slim node ./index.js`, + `docker run --rm --volume "$PWD":/usr/src/app -w /usr/src/app node:${version}-slim node ./index.cjs`, + { stdio: 'inherit' }, + ); + + childProcess.execSync( + `docker run --rm --volume "$PWD":/usr/src/app -w /usr/src/app node:${version}-slim node ./index.mjs`, { stdio: 'inherit' }, ); } diff --git a/integrationTests/ts/esm.ts b/integrationTests/ts/esm.ts new file mode 100644 index 0000000000..4554d1efec --- /dev/null +++ b/integrationTests/ts/esm.ts @@ -0,0 +1,38 @@ +import type { ExecutionResult } from 'graphql-esm/execution'; + +import { graphqlSync } from 'graphql-esm'; +import { + GraphQLString, + GraphQLSchema, + GraphQLObjectType, +} from 'graphql-esm/type'; + +const queryType: GraphQLObjectType = new GraphQLObjectType({ + name: 'Query', + fields: () => ({ + sayHi: { + type: GraphQLString, + args: { + who: { + type: GraphQLString, + defaultValue: 'World', + }, + }, + resolve(_root, args: { who: string }) { + return 'Hello ' + args.who; + }, + }, + }), +}); + +const schema: GraphQLSchema = new GraphQLSchema({ query: queryType }); + +const result: ExecutionResult = graphqlSync({ + schema, + source: ` + query helloWho($who: String){ + test(who: $who) + } + `, + variableValues: { who: 'Dolly' }, +}); diff --git a/integrationTests/ts/package.json b/integrationTests/ts/package.json index e7b66ef3b0..119388ec01 100644 --- a/integrationTests/ts/package.json +++ b/integrationTests/ts/package.json @@ -7,6 +7,7 @@ }, "dependencies": { "graphql": "file:../graphql.tgz", + "graphql-esm": "file:../graphql-esm.tgz", "typescript-4.4": "npm:typescript@4.4.x", "typescript-4.5": "npm:typescript@4.5.x", "typescript-4.6": "npm:typescript@4.6.x", diff --git a/integrationTests/webpack/entry-esm.mjs b/integrationTests/webpack/entry-esm.mjs new file mode 100644 index 0000000000..1dec59e043 --- /dev/null +++ b/integrationTests/webpack/entry-esm.mjs @@ -0,0 +1,13 @@ +// eslint-disable-next-line node/no-missing-import, import/no-unresolved +import { graphqlSync } from 'graphql-esm'; + +// eslint-disable-next-line node/no-missing-import, import/no-unresolved +import { buildSchema } from 'graphql-esm/utilities/buildASTSchema'; + +const schema = buildSchema('type Query { hello: String }'); + +export const result = graphqlSync({ + schema, + source: '{ hello }', + rootValue: { hello: 'world' }, +}); diff --git a/integrationTests/webpack/package.json b/integrationTests/webpack/package.json index 74a2502ff7..66dd836f1e 100644 --- a/integrationTests/webpack/package.json +++ b/integrationTests/webpack/package.json @@ -7,6 +7,7 @@ }, "dependencies": { "graphql": "file:../graphql.tgz", + "graphql-esm": "file:../graphql-esm.tgz", "webpack": "5.x.x", "webpack-cli": "4.x.x" } diff --git a/integrationTests/webpack/test.js b/integrationTests/webpack/test.js index 848971d7b1..e4b753b578 100644 --- a/integrationTests/webpack/test.js +++ b/integrationTests/webpack/test.js @@ -1,11 +1,20 @@ import assert from 'assert'; -import mainCJS from './dist/main.cjs'; +import cjs from './dist/main-cjs.cjs'; +import mjs from './dist/main-mjs.cjs'; -assert.deepStrictEqual(mainCJS.result, { +assert.deepStrictEqual(cjs.result, { data: { __proto__: null, hello: 'world', }, }); + +assert.deepStrictEqual(mjs.result, { + data: { + __proto__: null, + hello: 'world', + }, +}); + console.log('Test script: Got correct result from Webpack bundle!'); diff --git a/integrationTests/webpack/webpack.config.json b/integrationTests/webpack/webpack.config.json index 4d3276186c..8381df4cee 100644 --- a/integrationTests/webpack/webpack.config.json +++ b/integrationTests/webpack/webpack.config.json @@ -1,8 +1,11 @@ { "mode": "production", - "entry": "./entry.js", + "entry": { + "cjs": "./entry.js", + "mjs": "./entry-esm.mjs" + }, "output": { - "filename": "main.cjs", + "filename": "main-[name].cjs", "library": { "type": "commonjs2" } diff --git a/resources/build-npm.ts b/resources/build-npm.ts index aec7d99f48..4ccd249a76 100644 --- a/resources/build-npm.ts +++ b/resources/build-npm.ts @@ -4,6 +4,7 @@ import path from 'node:path'; import ts from 'typescript'; +import { changeExtensionInImportPaths } from './change-extension-in-import-paths.js'; import { inlineInvariant } from './inline-invariant.js'; import { readPackageJSON, @@ -12,17 +13,20 @@ import { writeGeneratedFile, } from './utils.js'; -buildPackage(); +console.log('\n./npmDist'); +buildPackage('./npmDist', false); showDirStats('./npmDist'); -function buildPackage() { - fs.rmSync('./npmDist', { recursive: true, force: true }); - fs.mkdirSync('./npmDist'); +console.log('\n./npmEsmDist'); +buildPackage('./npmEsmDist', true); +showDirStats('./npmEsmDist'); - fs.copyFileSync('./LICENSE', './npmDist/LICENSE'); - fs.copyFileSync('./README.md', './npmDist/README.md'); +function buildPackage(outDir: string, isESMOnly: boolean): void { + fs.rmSync(outDir, { recursive: true, force: true }); + fs.mkdirSync(outDir); - const { emittedTSFiles } = emitTSFiles('./npmDist'); + fs.copyFileSync('./LICENSE', `./${outDir}/LICENSE`); + fs.copyFileSync('./README.md', `./${outDir}/README.md`); const packageJSON = readPackageJSON(); @@ -39,7 +43,7 @@ function buildPackage() { // TODO: revisit once TS implements https://github.com/microsoft/TypeScript/issues/32166 const notSupportedTSVersionFile = 'NotSupportedTSVersion.d.ts'; fs.writeFileSync( - path.join('./npmDist', notSupportedTSVersionFile), + path.join(outDir, notSupportedTSVersionFile), // Provoke syntax error to show this message `"Package 'graphql' support only TS versions that are ${supportedTSVersions[0]}".`, ); @@ -49,19 +53,6 @@ function buildPackage() { '*': { '*': [notSupportedTSVersionFile] }, }; - packageJSON.exports = {}; - - for (const filepath of emittedTSFiles) { - if (path.basename(filepath) === 'index.js') { - const relativePath = './' + path.relative('./npmDist', filepath); - packageJSON.exports[path.dirname(relativePath)] = relativePath; - } - } - - // Temporary workaround to allow "internal" imports, no grantees provided - packageJSON.exports['./*.js'] = './*.js'; - packageJSON.exports['./*'] = './*.js'; - // TODO: move to integration tests const publishTag = packageJSON.publishConfig?.tag; assert(publishTag != null, 'Should have packageJSON.publishConfig defined!'); @@ -90,16 +81,51 @@ function buildPackage() { ); } + if (isESMOnly) { + packageJSON.exports = {}; + + const { emittedTSFiles } = emitTSFiles({ + outDir, + module: 'es2020', + extension: '.js', + }); + + for (const filepath of emittedTSFiles) { + if (path.basename(filepath) === 'index.js') { + const relativePath = './' + path.relative('./npmEsmDist', filepath); + packageJSON.exports[path.dirname(relativePath)] = relativePath; + } + } + + // Temporary workaround to allow "internal" imports, no grantees provided + packageJSON.exports['./*.js'] = './*.js'; + packageJSON.exports['./*'] = './*.js'; + + packageJSON.publishConfig.tag += '-esm'; + packageJSON.version += '+esm'; + } else { + delete packageJSON.type; + packageJSON.main = 'index'; + packageJSON.module = 'index.mjs'; + emitTSFiles({ outDir, module: 'commonjs', extension: '.js' }); + emitTSFiles({ outDir, module: 'es2020', extension: '.mjs' }); + } + // Should be done as the last step so only valid packages can be published - writeGeneratedFile('./npmDist/package.json', JSON.stringify(packageJSON)); + writeGeneratedFile(`./${outDir}/package.json`, JSON.stringify(packageJSON)); } // Based on https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#getting-the-dts-from-a-javascript-file -function emitTSFiles(outDir: string): { +function emitTSFiles(options: { + outDir: string; + module: string; + extension: string; +}): { emittedTSFiles: ReadonlyArray; } { + const { outDir, module, extension } = options; const tsOptions = readTSConfig({ - module: 'es2020', + module, noEmit: false, declaration: true, declarationDir: outDir, @@ -108,11 +134,12 @@ function emitTSFiles(outDir: string): { }); const tsHost = ts.createCompilerHost(tsOptions); - tsHost.writeFile = writeGeneratedFile; + tsHost.writeFile = (filepath, body) => + writeGeneratedFile(filepath.replace(/.js$/, extension), body); const tsProgram = ts.createProgram(['src/index.ts'], tsOptions, tsHost); const tsResult = tsProgram.emit(undefined, undefined, undefined, undefined, { - after: [inlineInvariant], + after: [changeExtensionInImportPaths({ extension }), inlineInvariant], }); assert( !tsResult.emitSkipped, diff --git a/resources/integration-test.ts b/resources/integration-test.ts index a03c9dbb4a..47afc844d6 100644 --- a/resources/integration-test.ts +++ b/resources/integration-test.ts @@ -11,10 +11,17 @@ describe('Integration Tests', () => { }); npm().run('build:npm'); + const distDir = localRepoPath('npmDist'); const archiveName = npm({ cwd: tmpDirPath(), quiet: true }).pack(distDir); fs.renameSync(tmpDirPath(archiveName), tmpDirPath('graphql.tgz')); + const esmDistDir = localRepoPath('npmEsmDist'); + const archiveEsmName = npm({ cwd: tmpDirPath(), quiet: true }).pack( + esmDistDir, + ); + fs.renameSync(tmpDirPath(archiveEsmName), tmpDirPath('graphql-esm.tgz')); + npm().run('build:deno'); function testOnNodeProject(projectName: string) { diff --git a/resources/utils.ts b/resources/utils.ts index 66b90e6e4c..7b726e02ce 100644 --- a/resources/utils.ts +++ b/resources/utils.ts @@ -198,7 +198,11 @@ interface PackageJSON { types?: string; typesVersions: { [ranges: string]: { [path: string]: Array } }; devDependencies?: { [name: string]: string }; - publishConfig?: { tag?: string }; + publishConfig: { tag: string }; + + // TODO: remove after we drop CJS support + main?: string; + module?: string; } export function readPackageJSON(