build standardized dist files
ADD
.eslintignore
+# dist
+dist/

MODIFY
.gitignore
.gitignore
@@ -59,3 +59,6 @@ typings/
# next.js build output
.next
+
+# dist
+dist/

DELETE
index-commonjs.js
-'use strict';
-
-// Adapted from SES/Caja - Copyright (C) 2011 Google Inc.
-// Copyright (C) 2018 Agoric
-
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// based upon:
-// https://github.com/google/caja/blob/master/src/com/google/caja/ses/startSES.js
-// https://github.com/google/caja/blob/master/src/com/google/caja/ses/repairES5.js
-// then copied from proposal-frozen-realms deep-freeze.js
-// then copied from SES/src/bundle/deepFreeze.js
-
-function makeHardener(initialFringe) {
- const { freeze, getOwnPropertyDescriptors, getPrototypeOf } = Object;
- const { ownKeys } = Reflect;
- // Objects that we won't freeze, either because we've frozen them already,
- // or they were one of the initial roots (terminals). These objects form
- // the "fringe" of the hardened object graph.
- const fringeSet = new WeakSet(initialFringe);
-
- function harden(root) {
- const toFreeze = new Set();
- const prototypes = new Map();
- const paths = new WeakMap();
-
- // If val is something we should be freezing but aren't yet,
- // add it to toFreeze.
- function enqueue(val, path) {
- if (Object(val) !== val) {
- // ignore primitives
- return;
- }
- const type = typeof val;
- if (type !== 'object' && type !== 'function') {
- // future proof: break until someone figures out what it should do
- throw new TypeError(`Unexpected typeof: ${type}`);
- }
- if (fringeSet.has(val) || toFreeze.has(val)) {
- // Ignore if this is an exit, or we've already visited it
- return;
- }
- // console.log(`adding ${val} to toFreeze`, val);
- toFreeze.add(val);
- paths.set(val, path);
- }
-
- function freezeAndTraverse(obj) {
- // Immediately freeze the object to ensure reactive
- // objects such as proxies won't add properties
- // during traversal, before they get frozen.
-
- // Object are verified before being enqueued,
- // therefore this is a valid candidate.
- // Throws if this fails (strict mode).
- freeze(obj);
-
- // we rely upon certain commitments of Object.freeze and proxies here
-
- // get stable/immutable outbound links before a Proxy has a chance to do
- // something sneaky.
- const proto = getPrototypeOf(obj);
- const descs = getOwnPropertyDescriptors(obj);
- const path = paths.get(obj) || 'unknown';
-
- // console.log(`adding ${proto} to prototypes under ${path}`);
- if (proto !== null && !prototypes.has(proto)) {
- prototypes.set(proto, path);
- paths.set(proto, `${path}.__proto__`);
- }
-
- ownKeys(descs).forEach(name => {
- const pathname = `${path}.${String(name)}`;
- // todo uncurried form
- // todo: getOwnPropertyDescriptors is guaranteed to return well-formed
- // descriptors, but they still inherit from Object.prototype. If
- // someone has poisoned Object.prototype to add 'value' or 'get'
- // properties, then a simple 'if ("value" in desc)' or 'desc.value'
- // test could be confused. We use hasOwnProperty to be sure about
- // whether 'value' is present or not, which tells us for sure that this
- // is a data property.
- const desc = descs[name];
- if ('value' in desc) {
- // todo uncurried form
- enqueue(desc.value, `${pathname}`);
- } else {
- enqueue(desc.get, `${pathname}(get)`);
- enqueue(desc.set, `${pathname}(set)`);
- }
- });
- }
-
- function dequeue() {
- // New values added before forEach() has finished will be visited.
- toFreeze.forEach(freezeAndTraverse); // todo curried forEach
- }
-
- function checkPrototypes() {
- prototypes.forEach((path, p) => {
- if (!(toFreeze.has(p) || fringeSet.has(p))) {
- // all reachable properties have already been frozen by this point
- throw new TypeError(
- `prototype ${p} of ${path} is not already in the fringeSet`,
- );
- }
- });
- }
-
- function commit() {
- // todo curried forEach
- // we capture the real WeakSet.prototype.add above, in case someone
- // changes it. The two-argument form of forEach passes the second
- // argument as the 'this' binding, so we add to the correct set.
- toFreeze.forEach(fringeSet.add, fringeSet);
- }
-
- enqueue(root);
- dequeue();
- // console.log("fringeSet", fringeSet);
- // console.log("prototype set:", prototypes);
- // console.log("toFreeze set:", toFreeze);
- checkPrototypes();
- commit();
-
- return root;
- }
-
- return harden;
-}
-
-module.exports = makeHardener;

MODIFY
package-lock.json
package-lock.json
"integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
"dev": true
},
+ "esm": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.7.tgz",
+ "integrity": "sha512-zsyD5gO8CY9dpK3IrdC4WHtvtHGXEFOpYA4zB+6p+Kygf3vv/6kF3YMEQLOArwKPPNvKt8gjI8UYhQW8bXM/YQ==",
+ "dev": true
+ },
"espree": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-5.0.0.tgz",

MODIFY
package.json
package.json
"name": "@agoric/make-hardener",
"version": "0.0.4",
"description": "Create a 'hardener' which freezes the API surface of a set of objects",
- "main": "index-commonjs.js",
- "module": "index.js",
+ "main": "dist/make-hardener.cjs.js",
+ "module": "dist/make-hardener.esm.js",
+ "browser": "dist/make-hardener.umd.js",
"scripts": {
- "test": "rollup index.js --file index-commonjs.js --format cjs && node test/test.js",
+ "test": "node -r esm test/test.js",
+ "build": "rollup -c",
"lint-fix": "eslint --fix '**/*.{js,jsx}'",
- "lint-check": "eslint '**/*.{js,jsx}'",
- "create-cjs": "rollup index.js --file index-commonjs.js --format cjs"
+ "lint-check": "eslint '**/*.{js,jsx}'"
},
"repository": {
"type": "git",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-react": "^7.12.4",
+ "esm": "^3.2.7",
"prettier": "1.16.4",
"rollup": "^1.2.2",
"tape": "^4.9.2"
"directories": {
"test": "test"
},
- "keywords": []
+ "keywords": [
+ "deepFreeze",
+ "harden",
+ "Object.freeze",
+ "freeze"
+ ],
+ "files": [
+ "dist"
+ ]
}

MODIFY
release-checklist.md
release-checklist.md
1. Bump the version in `package.json`
2. `npm install` to update `package-lock.json`'s version
-3. run `npm run create-cjs` to generate the updated CommonJS module.
+3. run `npm run build` to generate the `dist/` files.
+

ADD
rollup.config.js
+export default [
+ {
+ input: 'src/index.js',
+ output: [
+ {
+ file: 'dist/make-hardener.umd.js',
+ format: 'umd',
+ name: 'MakeHardener',
+ },
+ {
+ file: 'dist/make-hardener.esm.js',
+ format: 'esm',
+ },
+ {
+ file: 'dist/make-hardener.cjs.js',
+ format: 'cjs',
+ },
+ ],
+ },
+];

RENAME
index.js
src/index.js
MODIFY
test/test.js
test/test.js
-const test = require('tape');
-const makeHardener = require('../index-commonjs.js');
+import test from 'tape';
+import makeHardener from '../src/index';
test('makeHardener', t => {
const h = makeHardener([Object.prototype]);

build standardized dist files