Skip to content

Commit

Permalink
Add build process to all addons (#9946)
Browse files Browse the repository at this point in the history
* Build createFragment addon

* Tack the addon onto React.addons object

* Generalize build process for all addons

* Fix lint

* Fix lint again
  • Loading branch information
gaearon authored Jun 13, 2017
1 parent 9ac30f5 commit 129307f
Show file tree
Hide file tree
Showing 32 changed files with 4,722 additions and 279 deletions.
4 changes: 2 additions & 2 deletions addons/create-react-class/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ var factory = require('./factory');

if (typeof React === 'undefined') {
throw Error(
'createReactClass could not find the React object. If you are using script tags, ' +
'make sure that React is being loaded before createReactClass.'
'create-react-class could not find the React object. If you are using script tags, ' +
'make sure that React is being loaded before create-react-class.'
);
}

Expand Down
73 changes: 73 additions & 0 deletions addons/postbuild.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* Copyright 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/

'use strict';

var fs = require('fs');
var path = require('path');

// This lets us import Webpack config without crashing
process.env.NODE_ENV = 'development';

// This script runs from the addon folder
var exportName = require(path.resolve(process.cwd(), './webpack.config')).output
.library;
var packageName = path.basename(process.cwd());

if (packageName.indexOf('react-addons') !== 0) {
throw new Error(
'Only run this script for packages that used to be published as addons.'
);
}

// Inputs
// DEV: root["exportName"] = factory(root["React"])
// PROD: e.exportName=t(e.React)
var find = new RegExp(
'((?!exports)\\b\\w+)(\\["' +
exportName +
'"\\]|\\.' +
exportName +
')\\s*=\\s*(\\w+)\\((.*)\\)'
);
// Outputs
// DEV: (root.React ? (root.React.addons = root.React.addons || {}) : /* throw */).exportName = factory(/* ... */);
// PROD: (e.React ? (e.React.addons = e.React.addons || {}) : /* throw */).exportName = t(/* ... */)
var throwIIFE = [
'(function(){',
'throw new Error("' +
packageName +
' could not find the React object. If you are using script tags, make sure that React is being loaded before ' +
packageName +
'.")',
'})()',
].join('');
var replace =
'($1.React?($1.React.addons=$1.React.addons||{}):' +
throwIIFE +
').' +
exportName +
'=$3($4)';

console.log('Tweaking the development UMD...');
var devUMD = fs.readFileSync('./' + packageName + '.js', 'utf8').toString();
devUMD = devUMD.replace(find, replace);
fs.writeFileSync('./' + packageName + '.js', devUMD);

console.log('Tweaking the production UMD...');
var prodUMD = fs
.readFileSync('./' + packageName + '.min.js', 'utf8')
.toString();
prodUMD = prodUMD.replace(find, replace);
fs.writeFileSync('./' + packageName + '.min.js', prodUMD);

console.log('Done.');
console.log('Note that you need to manually test the UMD builds.');
2 changes: 2 additions & 0 deletions addons/react-addons-create-fragment/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
react-addons-create-fragment.js
react-addons-create-fragment.min.js
7 changes: 5 additions & 2 deletions addons/react-addons-create-fragment/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@
"react-addons-create-fragment.min.js"
],
"scripts": {
"test": "jest",
"prepublish": "npm test"
"test": "TEST_ENTRY=./index.js jest",
"build:dev": "NODE_ENV=development webpack && TEST_ENTRY=./react-addons-create-fragment.js jest",
"build:prod": "NODE_ENV=production webpack && NODE_ENV=production TEST_ENTRY=./react-addons-create-fragment.min.js jest",
"build": "npm run build:dev && npm run build:prod && node ../postbuild.js",
"prepublish": "npm test && npm run build"
},
"devDependencies": {
"jest": "^19.0.2",
Expand Down
76 changes: 61 additions & 15 deletions addons/react-addons-create-fragment/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,78 @@
var React;
var createReactFragment;

// For testing DOM Fiber.
global.requestAnimationFrame = function(callback) {
setTimeout(callback);
// Catch stray warnings
var env = jasmine.getEnv();
var callCount = 0;
var oldError = console.error;
var newError = function() {
callCount++;
oldError.apply(this, arguments);
};

global.requestIdleCallback = function(callback) {
setTimeout(() => {
callback({
timeRemaining() {
return Infinity;
},
});
console.error = newError;
env.beforeEach(() => {
callCount = 0;
jasmine.addMatchers({
toBeReset() {
return {
compare(actual) {
if (actual !== newError && !jasmine.isSpy(actual)) {
return {
pass: false,
message: 'Test did not tear down console.error mock properly.',
};
}
return {pass: true};
},
};
},
toNotHaveBeenCalled() {
return {
compare(actual) {
return {
pass: callCount === 0,
message: 'Expected test not to warn. If the warning is expected, mock ' +
"it out using spyOn(console, 'error'); and test that the " +
'warning occurs.',
};
},
};
},
});
};
});
env.afterEach(() => {
expect(console.error).toBeReset();
expect(console.error).toNotHaveBeenCalled();
});

const expectDev = function expectDev(actual) {
// Suppress warning expectations for prod builds
function suppressDevMatcher(obj, name) {
const original = obj[name];
obj[name] = function devMatcher() {
try {
original.apply(this, arguments);
} catch (e) {
// skip
}
};
}
function expectDev(actual) {
const expectation = expect(actual);
if (process.env.NODE_ENV === 'production') {
Object.keys(expectation).forEach(name => {
suppressDevMatcher(expectation, name);
suppressDevMatcher(expectation.not, name);
});
}
return expectation;
};
}

describe('createReactFragment', () => {
beforeEach(() => {
jest.resetModules();

React = require('react');
createReactFragment = require('./index');
createReactFragment = require(process.env.TEST_ENTRY);
});

it('warns for numeric keys on objects as children', () => {
Expand Down
63 changes: 63 additions & 0 deletions addons/react-addons-create-fragment/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/

'use strict';

const webpack = require('webpack');

let __DEV__;
switch (process.env.NODE_ENV) {
case 'development':
__DEV__ = true;
break;
case 'production':
__DEV__ = false;
break;
default:
throw new Error('Unknown environment.');
}

module.exports = {
entry: './index',
output: {
library: 'createFragment',
libraryTarget: 'umd',
filename: __DEV__
? 'react-addons-create-fragment.js'
: 'react-addons-create-fragment.min.js',
},
externals: {
react: {
root: 'React',
commonjs2: 'react',
commonjs: 'react',
amd: 'react',
},
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': __DEV__ ? '"development"' : '"production"',
}),
].concat(
__DEV__
? []
: [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
},
output: {
comments: false,
},
}),
]
),
};
2 changes: 2 additions & 0 deletions addons/react-addons-linked-state-mixin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
react-addons-linked-state-mixin.js
react-addons-linked-state-mixin.min.js
14 changes: 9 additions & 5 deletions addons/react-addons-linked-state-mixin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
"object-assign": "^4.1.0"
},
"scripts": {
"test": "jest",
"prepublish": "npm test"
"test": "TEST_ENTRY=./index.js jest",
"build:dev": "NODE_ENV=development webpack && TEST_ENTRY=./react-addons-linked-state-mixin.js jest",
"build:prod": "NODE_ENV=production webpack && NODE_ENV=production TEST_ENTRY=./react-addons-linked-state-mixin.min.js jest",
"build": "npm run build:dev && npm run build:prod && node ../postbuild.js",
"prepublish": "npm test && npm run build"
},
"files": [
"LICENSE",
Expand All @@ -25,9 +28,10 @@
"react-addons-linked-state-mixin.min.js"
],
"devDependencies": {
"create-react-class": "^15.5.4",
"jest": "^19.0.2",
"react": "^15.4.2",
"react-addons-test-utils": "15.4.2",
"react-dom": "^15.4.2"
"react": "^15.5.4",
"react-dom": "^15.5.4",
"webpack": "^2.6.1"
}
}
63 changes: 47 additions & 16 deletions addons/react-addons-linked-state-mixin/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,40 +12,71 @@
'use strict';

let LinkedStateMixin;
let createReactClass;
let React;
let ReactDOM;
let ReactTestUtils;

// For testing DOM Fiber.
global.requestAnimationFrame = function(callback) {
setTimeout(callback);
// Catch stray warnings
var env = jasmine.getEnv();
var callCount = 0;
var oldError = console.error;
var newError = function() {
callCount++;
oldError.apply(this, arguments);
};

global.requestIdleCallback = function(callback) {
setTimeout(() => {
callback({
timeRemaining() {
return Infinity;
},
});
console.error = newError;
env.beforeEach(() => {
callCount = 0;
jasmine.addMatchers({
toBeReset() {
return {
compare(actual) {
if (actual !== newError && !jasmine.isSpy(actual)) {
return {
pass: false,
message: 'Test did not tear down console.error mock properly.',
};
}
return {pass: true};
},
};
},
toNotHaveBeenCalled() {
return {
compare(actual) {
return {
pass: callCount === 0,
message: 'Expected test not to warn. If the warning is expected, mock ' +
"it out using spyOn(console, 'error'); and test that the " +
'warning occurs.',
};
},
};
},
});
};
});
env.afterEach(() => {
expect(console.error).toBeReset();
expect(console.error).toNotHaveBeenCalled();
});

describe('LinkedStateMixin', () => {
beforeEach(() => {
jest.resetModules();

createReactClass = require('create-react-class');
React = require('react');
ReactDOM = require('react-dom');
ReactTestUtils = require('react-addons-test-utils');
LinkedStateMixin = require('./index');
ReactTestUtils = require('react-dom/test-utils');
LinkedStateMixin = require(process.env.TEST_ENTRY);
});

// https://facebook.github.io/react/docs/two-way-binding-helpers.html#linkedstatemixin-before-and-after
it('should work with valueLink', () => {
spyOn(console, 'error'); // Ignore deprecated valueLink message for now

const WithLink = React.createClass({
const WithLink = createReactClass({
mixins: [LinkedStateMixin],
getInitialState: function() {
return {message: 'Hello!'};
Expand All @@ -70,7 +101,7 @@ describe('LinkedStateMixin', () => {

// https://facebook.github.io/react/docs/two-way-binding-helpers.html#linkedstatemixin-without-valuelink
it('should work without valueLink', () => {
const WithoutLink = React.createClass({
const WithoutLink = createReactClass({
mixins: [LinkedStateMixin],
getInitialState: function() {
return {message: 'Hello!'};
Expand Down
Loading

0 comments on commit 129307f

Please sign in to comment.