Skip to content

Commit

Permalink
ECMAScript Module Exports (#13)
Browse files Browse the repository at this point in the history
* Use ECMAScript module format for .mjs.

* Update rollup_bundle tests with Jasmine specs.

* Update comment and fix grpc service exports.

* Add tests for GRPC service exports.

* Add exception for nexted exports and example to tests.
  • Loading branch information
awmichel authored and Dig-Doug committed Dec 7, 2019
1 parent 2a29323 commit 0ae5d5a
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 36 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"husky": "^3.0.9",
"rollup": "^1.25.1",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"ts-protoc-gen": "^0.11.0",
"typescript": "^3.6.4"
},
Expand Down
49 changes: 34 additions & 15 deletions src/change_import_style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function main() {
const umdContents = convertToUmd(args, initialContents);
fs.writeFileSync(args.output_umd_path, umdContents, 'utf8');

const commonJsContents = processCommonJs(args, initialContents);
const commonJsContents = convertToESM(args, initialContents);
fs.writeFileSync(args.output_es6_path, commonJsContents, 'utf8');
}

Expand Down Expand Up @@ -62,27 +62,46 @@ function convertToUmd(args: any, initialContents: string): string {
}, initialContents);
}

function processCommonJs(args: any, initialContents: string): string {
// Rollup can't resolve the commonjs exports when using goog.object.extend so we replace it with:
// 'exports.MyProto = proto.namespace.MyProto;'
// Converts the CommonJS format from protoc to the ECMAScript Module format.
// Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
function convertToESM(args: any, initialContents: string): string {
const replaceGoogExtendWithExports = (contents: string) => {
const googSymbolRegex = /goog.exportSymbol\('(.*\.([A-z0-9_]+))',.*;/g;
let match;
const symbols = [];
while (match = googSymbolRegex.exec(initialContents)) {
symbols.push([match[2], match[1]]);
}

const exportSymbols = symbols.reduce((currentSymbols, symbol) => {
return currentSymbols + `exports.${symbol[0]} = ${symbol[1]};\n`;
}, '');
return contents.replace(/goog.object.extend\(exports, .*;/g, `${exportSymbols}`);
return contents.replace(/goog\.object\.extend\(exports, ([\w\.]+)\);/g, (_, packageName: string) => {
const exportSymbols = /goog\.exportSymbol\('([\w\.]+)',.*\);/g;
const symbols = [];

let match: RegExpExecArray;
while (match = exportSymbols.exec(initialContents)) {
// We want to ignore embedded export targets, IE: `DeliveryPerson.DataCase`.
const exportTarget = match[1].substr(packageName.length + 1);
if (!exportTarget.includes('.')) {
symbols.push(exportTarget);
}
}

return `export const { ${symbols.join(', ')} } = ${packageName}`;
});
};

const replaceRequiresWithImports = (contents: string) => {
return contents.replace(/var ([\w\d_]+) = require\((['"][\w\d@/_-]+['"])\);/g, 'import * as $1 from $2;');
};

const replaceRequiresWithSubpackageImports = (contents: string) => {
return contents.replace(/var ([\w\d_]+) = require\((['"][\w\d@/_-]+['"])\)\.([\w\d_]+);/g, 'import * as $1 from $2;')
}

const replaceCJSExportsWithECMAExports = (contents: string) => {
return contents.replace(/exports\.([\w\d_]+) = .*;/g, 'export { $1 };')
}

const transformations: ((c: string) => string)[] = [
replaceRecursiveFilePaths(args),
removeJsExtensionsFromRequires,
replaceGoogExtendWithExports,
replaceRequiresWithImports,
replaceRequiresWithSubpackageImports,
replaceCJSExportsWithECMAExports,
];
return transformations.reduce((currentContents, transform) => {
return transform(currentContents);
Expand Down
14 changes: 14 additions & 0 deletions test/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ rollup_bundle(
deps = [
":test_bundling_lib",
],
format = "cjs",
)

ts_library(
Expand All @@ -118,7 +119,20 @@ ts_library(
tsconfig = ":test_bundling_tsconfig.json",
deps = [
"//test/proto:naming_styles_ts_proto",
"//test/proto/common:pizza_ts_proto",
"//test/proto:pizza_service_ts_proto",
"//test/proto/common:delivery_person_ts_proto",
"@npm//google-protobuf",
"@npm//@improbable-eng/grpc-web",
],
)

jasmine_node_test(
name = "rollup_test",
srcs = [
":rollup_test.spec.js"
],
data = [
":test_es6_bundling"
]
)
6 changes: 6 additions & 0 deletions test/proto/common/delivery_person.proto
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@ message DeliveryPerson {

// The list of pizzas the delivery person has left to deliver.
repeated Pizza pizzas = 2;

// Test nested structures inside protobufs.
oneof data {
string id = 3;
int64 id_v2 = 4;
}
}
9 changes: 8 additions & 1 deletion test/rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
const commonjs = require('rollup-plugin-commonjs');
const nodeRequire = require('rollup-plugin-node-resolve');

module.exports = {
plugins: [
nodeRequire(),
commonjs({
extensions: ['.js', '.mjs'],
// Temporary fix until https://github.com/improbable-eng/grpc-web/issues/369 is resolved.
namedExports: {
'./node_modules/@improbable-eng/grpc-web/dist/grpc-web-client.js': [
'grpc',
],
}
}),
],
};
47 changes: 47 additions & 0 deletions test/rollup_test.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const bundle = require('rules_typescript_proto/test/test_es6_bundling/');

describe('Rollup', () => {
it('should define Pizza with protobuf API', () => {
expect(bundle.Pizza).toBeDefined();

const pizza = new bundle.Pizza();
pizza.setSize(bundle.PizzaSize.PIZZA_SIZE_LARGE);

expect(pizza.getSize()).toBe(bundle.PizzaSize.PIZZA_SIZE_LARGE);
expect(Array.isArray(pizza.getToppingIdsList())).toBe(true);
});

it('should define DeliveryPerson', () => {
expect(bundle.DeliveryPerson).toBeDefined();
expect(new bundle.DeliveryPerson()).toBeTruthy();
});

it('should define PizzaService', () => {
expect(bundle.PizzaService).toBeDefined();
expect(bundle.PizzaService.serviceName).toBe('test.bazel.proto.PizzaService');
expect(bundle.PizzaService.OrderPizza.methodName).toBe('OrderPizza');
});

it('should define PizzaServiceClient', () => {
expect(bundle.PizzaServiceClient).toBeDefined();
const client = new bundle.PizzaServiceClient('http://localhost', {});
expect(typeof client.orderPizza).toBe('function');
});

it('should follow expected naming styles', () => {
expect(new bundle.alllowercase().setTest(1)).toBeTruthy();
expect(new bundle.ALLUPPERCASE().setTest(1)).toBeTruthy();
expect(new bundle.lowerCamelCase().setTest(1)).toBeTruthy();
expect(new bundle.UpperCamelCase().setTest(1)).toBeTruthy();
expect(new bundle.snake_case_snake_case().setTest(1)).toBeTruthy();
expect(new bundle.Upper_snake_Case().setTest(1)).toBeTruthy();
expect(new bundle.M2M().setTest(1)).toBeTruthy();
expect(new bundle.M_2M().setTest(1)).toBeTruthy();
expect(new bundle.M2_M().setTest(1)).toBeTruthy();
expect(new bundle.M2M_().setTest(1)).toBeTruthy();
expect(new bundle.m_22M().setTest(1)).toBeTruthy();
expect(new bundle.m42_M().setTest(1)).toBeTruthy();
expect(new bundle.m24M_().setTest(1)).toBeTruthy();
expect(new bundle.M9().setTest(1)).toBeTruthy();
});
});
24 changes: 4 additions & 20 deletions test/test_bundling.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,4 @@
import {DeliveryPerson} from 'rules_typescript_proto/test/proto/common/delivery_person_pb';
import {alllowercase, ALLUPPERCASE, lowerCamelCase, m24M_, M2_M, M2M, M2M_, m42_M, M9, m_22M, M_2M, snake_case_snake_case, Upper_snake_Case, UpperCamelCase,} from 'rules_typescript_proto/test/proto/naming_styles_pb';

const person = new DeliveryPerson();
console.log(person);

console.log(new alllowercase().setTest(1));
console.log(new ALLUPPERCASE().setTest(1));
console.log(new lowerCamelCase().setTest(1));
console.log(new UpperCamelCase().setTest(1));
console.log(new snake_case_snake_case().setTest(1));
console.log(new Upper_snake_Case().setTest(1));
console.log(new M2M().setTest(1));
console.log(new M_2M().setTest(1));
console.log(new M2_M().setTest(1));
console.log(new M2M_().setTest(1));
console.log(new m_22M().setTest(1));
console.log(new m42_M().setTest(1));
console.log(new m24M_().setTest(1));
console.log(new M9().setTest(1));
export {alllowercase, ALLUPPERCASE, lowerCamelCase, m24M_, M2_M, M2M, M2M_, m42_M, M9, m_22M, M_2M, snake_case_snake_case, Upper_snake_Case, UpperCamelCase,} from 'rules_typescript_proto/test/proto/naming_styles_pb';
export { DeliveryPerson } from 'rules_typescript_proto/test/proto/common/delivery_person_pb';
export { Pizza, PizzaSize } from 'rules_typescript_proto/test/proto/common/pizza_pb';
export { PizzaService, PizzaServiceClient } from 'rules_typescript_proto/test/proto/pizza_service_pb_service';
35 changes: 35 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,13 @@
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==

"@types/[email protected]":
version "0.0.8"
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194"
integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==
dependencies:
"@types/node" "*"

abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
Expand Down Expand Up @@ -419,6 +426,11 @@ buffer-from@^1.0.0:
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==

builtin-modules@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484"
integrity sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==

[email protected]:
version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
Expand Down Expand Up @@ -1467,6 +1479,11 @@ is-glob@^4.0.0:
dependencies:
is-extglob "^2.1.1"

is-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=

is-number@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
Expand Down Expand Up @@ -2539,6 +2556,13 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.0:
dependencies:
path-parse "^1.0.6"

resolve@^1.11.1:
version "1.13.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.13.0.tgz#e879eb397efb40511056ede7300b6ac28c51290b"
integrity sha512-HHZ3hmOrk5SvybTb18xq4Ek2uLqLO5/goFCYUyvn26nWox4hdlKlfC/+dChIZ6qc4ZeYcN9ekTz0yyHsFgumMw==
dependencies:
path-parse "^1.0.6"

ret@~0.1.10:
version "0.1.15"
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
Expand Down Expand Up @@ -2567,6 +2591,17 @@ rollup-plugin-commonjs@^10.1.0:
resolve "^1.11.0"
rollup-pluginutils "^2.8.1"

rollup-plugin-node-resolve@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz#730f93d10ed202473b1fb54a5997a7db8c6d8523"
integrity sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==
dependencies:
"@types/resolve" "0.0.8"
builtin-modules "^3.1.0"
is-module "^1.0.0"
resolve "^1.11.1"
rollup-pluginutils "^2.8.1"

rollup-pluginutils@^2.8.1:
version "2.8.2"
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e"
Expand Down

0 comments on commit 0ae5d5a

Please sign in to comment.