From 615234dd5c514ac82038ff264382a48ffbf8ad18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Fri, 23 Oct 2015 17:46:04 +0200 Subject: [PATCH 1/4] 2.0.0: GL.Surface, GL.Node & dep on React 0.14.0 --- package.json | 3 + src/Node.js | 27 +++ src/Shaders.js | 49 +++++ src/Uniform.js | 23 +++ src/createComponent.js | 65 +++---- src/createComponentDeprecated.js | 16 -- src/createShaders.js | 36 ---- src/{createView.js => createSurface.js} | 78 +++++--- src/createUniform.js | 21 --- src/data/build.js | 199 +++++++++----------- src/data/findGLNodeInGLComponentChildren.js | 10 + src/data/index.js | 2 +- src/data/pickReactFirstChild.js | 7 + src/data/resolve.js | 2 +- src/data/unfoldGLComponent.js | 12 ++ src/glViewMethods.json | 4 - src/index.js | 16 +- 17 files changed, 306 insertions(+), 264 deletions(-) create mode 100644 src/Node.js create mode 100644 src/Shaders.js create mode 100644 src/Uniform.js delete mode 100644 src/createComponentDeprecated.js delete mode 100644 src/createShaders.js rename src/{createView.js => createSurface.js} (50%) delete mode 100644 src/createUniform.js create mode 100644 src/data/findGLNodeInGLComponentChildren.js create mode 100644 src/data/pickReactFirstChild.js create mode 100644 src/data/unfoldGLComponent.js delete mode 100644 src/glViewMethods.json diff --git a/package.json b/package.json index 39eb5a8..e53e4f0 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,9 @@ "url": "https://github.com/ProjectSeptemberInc/gl-react-core/issues" }, "homepage": "https://github.com/ProjectSeptemberInc/gl-react-core#readme", + "peerDependencies": { + "react": "^0.14.0" + }, "dependencies": { "babelify": "^6.3.0", "envify": "^3.4.0", diff --git a/src/Node.js b/src/Node.js new file mode 100644 index 0000000..828aa5e --- /dev/null +++ b/src/Node.js @@ -0,0 +1,27 @@ +const React = require("react"); +const { Component, PropTypes } = React; +const invariant = require("invariant"); + +class Node extends Component { + render () { + invariant( + false, + "GL.Node elements can only be used as children of GL.Surface / GL.Node and should not be rendered" + ); + } +} + +Node.isGLNode = true; + +Node.displayName = "GL.Node"; + +Node.propTypes = { + shader: PropTypes.number.isRequired, + uniforms: PropTypes.object, + children: PropTypes.node, + width: PropTypes.number, + height: PropTypes.number, + preload: PropTypes.bool +}; + +module.exports = Node; diff --git a/src/Shaders.js b/src/Shaders.js new file mode 100644 index 0000000..1864f88 --- /dev/null +++ b/src/Shaders.js @@ -0,0 +1,49 @@ +const EventEmitter = require("events"); +const invariant = require("invariant"); + +let _uid = 1; +const names = {}; +const shaders = {}; + +const Shaders = { + create (obj) { + invariant(typeof obj === "object", "config must be an object"); + const result = {}; + for (let key in obj) { + const shader = obj[key]; + invariant(typeof shader === "object" && typeof shader.frag === "string", + "invalid shader given to Shaders.create(). A valid shader is a { frag: String }"); + const id = _uid ++; + if (!shader.name) shader.name = key; + names[id] = shader.name; + shaders[id] = shader; + this.emit("add", id, shader); + result[key] = id; + } + return result; + }, + remove (id) { + invariant(id in shaders, "There is no such shader '%s'", id); + delete shaders[id]; + delete names[id]; + this.emit("remove", id); + }, + get (id) { + return shaders[id]; + }, + getName (id) { + return names[id]; + }, + list () { + return Object.keys(names); + }, + exists (id) { + return typeof id === "number" && id >= 1 && id < _uid; + }, + + ...EventEmitter.prototype +}; + +EventEmitter.call(Shaders); + +module.exports = Object.freeze(Shaders); diff --git a/src/Uniform.js b/src/Uniform.js new file mode 100644 index 0000000..893bb6b --- /dev/null +++ b/src/Uniform.js @@ -0,0 +1,23 @@ +const React = require("react"); +const { Component, PropTypes } = React; +const invariant = require("invariant"); + +class Uniform extends Component { + render () { + invariant( + false, + "GL.Uniform elements are for GL.Node configuration only and should not be rendered" + ); + } +} + +Uniform.isGLUniform = true; + +Uniform.displayName = "GL.Uniform"; + +Uniform.propTypes = { + children: PropTypes.any.isRequired, + name: PropTypes.string.isRequired +}; + +module.exports = Uniform; diff --git a/src/createComponent.js b/src/createComponent.js index 3338630..ba5f19d 100644 --- a/src/createComponent.js +++ b/src/createComponent.js @@ -1,39 +1,36 @@ +const React = require("react"); const invariant = require("invariant"); -const glViewMethods = require("./glViewMethods"); - -module.exports = function (React, View) { - function createComponent (renderGLView, staticFields) { - invariant(typeof renderGLView === "function", - "GL.createComponent(props => glview) must have a function in parameter"); - class GLComponent extends React.Component { - constructor (props, context) { - super(props, context); - glViewMethods.forEach(this._delegateMethod, this); - } - _delegateMethod (methodname) { - const self = this; - this[methodname] = function () { - const glViewRef = self.refs._; - invariant(glViewRef, "glView has been rendered"); - return glViewRef[methodname].apply(glViewRef, arguments); - }; - } - render () { - const glView = renderGLView(this.props); - invariant(glView && (glView.type === View || glView.type.isGLComponent), - "The GL.createComponent function parameter must return a GL.View or another GL Component"); - return React.cloneElement(glView, { ...glView.props, ref: "_" }); - } + +module.exports = function createComponent (renderGLNode, staticFields) { + + invariant(typeof renderGLNode === "function", + "GL.createComponent(props => glnode) must have a function in parameter"); + + class GLComponent extends React.Component { + render () { + const glNode = renderGLNode(this.props); + + invariant(glNode && glNode.type && (glNode.type.isGLNode || glNode.type.isGLComponent), + "The GL.createComponent function parameter must return a GL.Node or "+ + "another GL Component"); + + return glNode; } - GLComponent.isGLComponent = true; - GLComponent.displayName = renderGLView.name || ""; - if (staticFields) { - invariant(typeof staticFields === "object", "second parameter of createComponent must be an object of static fields to set in the React component. (example: propTypes, displayName)"); - for (let key in staticFields) { - GLComponent[key] = staticFields[key]; - } + } + + GLComponent.isGLComponent = true; + + GLComponent.displayName = renderGLNode.name || ""; + + if (staticFields) { + invariant(typeof staticFields === "object", + "second parameter of createComponent must be an object of static fields "+ + "to set in the React component. (example: propTypes, displayName)"); + + for (let key in staticFields) { + GLComponent[key] = staticFields[key]; } - return GLComponent; } - return createComponent; + + return GLComponent; }; diff --git a/src/createComponentDeprecated.js b/src/createComponentDeprecated.js deleted file mode 100644 index 7e495d6..0000000 --- a/src/createComponentDeprecated.js +++ /dev/null @@ -1,16 +0,0 @@ -const invariant = require("invariant"); -const glViewMethods = require("./glViewMethods"); -module.exports = function (React) { - class GLComponent extends React.Component { - constructor (props, context) { - super(props, context); - glViewMethods.forEach(methodname => { - if (!this[methodname]) this[methodname] = () => invariant(true, "'%s' method is not available in deprecated GL.Component. Use GL.createComponent(props => glView) instead"); - }); - if (process.env.NODE_ENV !== "production") - console.error("GL.Component class is deprecated. Use GL.createComponent(props => glView) function instead"); // eslint-disable-line no-console - } - } - GLComponent.isGLComponent = true; - return GLComponent; -}; diff --git a/src/createShaders.js b/src/createShaders.js deleted file mode 100644 index 676c3f4..0000000 --- a/src/createShaders.js +++ /dev/null @@ -1,36 +0,0 @@ -const invariant = require("invariant"); - -module.exports = function (recordShader) { - let _uid = 1; - - const names = {}; - - const Shaders = { - create: function (obj) { - invariant(typeof obj === "object", "config must be an object"); - const result = {}; - for (let key in obj) { - const shader = obj[key]; - invariant(typeof shader === "object" && typeof shader.frag === "string", - "invalid shader given to Shaders.create(). A valid shader is a { frag: String }"); - const id = _uid ++; - if (!shader.name) shader.name = key; - names[id] = shader.name; - recordShader(id, shader); - result[key] = id; - } - return result; - }, - getName: function (id) { - return names[id]; - }, - list: function () { - return Object.keys(names); - }, - exists: function (id) { - return typeof id === "number" && id >= 1 && id < _uid; - } - }; - - return Shaders; -}; diff --git a/src/createView.js b/src/createSurface.js similarity index 50% rename from src/createView.js rename to src/createSurface.js index b1e4458..217adb3 100644 --- a/src/createView.js +++ b/src/createSurface.js @@ -1,23 +1,23 @@ +const React = require("react"); +const { + Component, + PropTypes +} = React; const invariant = require("invariant"); -const { fill, resolve, createBuild } = require("./data"); +const { fill, resolve, build } = require("./data"); +const findGLNodeInGLComponentChildren = require("./data/findGLNodeInGLComponentChildren"); function logResult (data, contentsVDOM) { if (typeof console !== "undefined" && console.debug // eslint-disable-line ) { - console.debug("GL.View rendered with", data, contentsVDOM); // eslint-disable-line no-console + console.debug("GL.Surface rendered with", data, contentsVDOM); // eslint-disable-line no-console } } -module.exports = function (React, Shaders, Uniform, renderVcontainer, renderVcontent, renderVGL) { - const { - Component, - PropTypes - } = React; +module.exports = function (renderVcontainer, renderVcontent, renderVGL) { - let build; // will be set after GLView class defined. - - class GLView extends Component { + class GLSurface extends Component { constructor (props, context) { super(props, context); this._renderId = 1; @@ -34,27 +34,38 @@ module.exports = function (React, Shaders, Uniform, renderVcontainer, renderVcon render() { const renderId = this._renderId ++; const props = this.props; - const { style, width, height, children, shader, uniforms, debug, preload, opaque, visibleContent, eventsThrough, ...restProps } = props; + const { + style, + width, + height, + children, + debug, + preload, + opaque, + visibleContent, + eventsThrough, + ...restProps + } = props; - invariant(width && height && width>0 && height>0, "width and height are required for the root GLView"); + const { via, childGLNode } = + findGLNodeInGLComponentChildren(children); - const {data, contentsVDOM, imagesToPreload} = + const { data, contentsVDOM, imagesToPreload } = resolve( fill( build( - shader, - uniforms, + childGLNode, width, height, - children, - preload||false, - []))); + preload, + via))); if (debug) logResult(data, contentsVDOM); return renderVcontainer( { width, height, style, visibleContent, eventsThrough }, - contentsVDOM.map((vdom, i) => renderVcontent(data.width, data.height, i, vdom, { visibleContent })), + contentsVDOM.map((vdom, i) => + renderVcontent(data.width, data.height, i, vdom, { visibleContent })), renderVGL({ ...restProps, // eslint-disable-line no-undef width, @@ -71,23 +82,28 @@ module.exports = function (React, Shaders, Uniform, renderVcontainer, renderVcon } } - GLView.displayName = "GL.View"; - GLView.propTypes = { - shader: PropTypes.number.isRequired, - width: PropTypes.number, - height: PropTypes.number, - uniforms: PropTypes.object, + GLSurface.displayName = "GL.Surface"; + + GLSurface.propTypes = { + width: PropTypes.number.isRequired, + height: PropTypes.number.isRequired, + children: PropTypes.element.isRequired, opaque: PropTypes.bool, preload: PropTypes.bool, autoRedraw: PropTypes.bool, eventsThrough: PropTypes.bool, - visibleContent: PropTypes.bool - }; - GLView.defaultProps = { - opaque: true + visibleContent: PropTypes.bool, + onLoad: PropTypes.func, + onProgress: PropTypes.func }; - build = createBuild(React, Shaders, Uniform, GLView); + GLSurface.defaultProps = { + opaque: true, + preload: false, + autoRedraw: false, + eventsThrough: false, + visibleContent: false + }; - return GLView; + return GLSurface; }; diff --git a/src/createUniform.js b/src/createUniform.js deleted file mode 100644 index c8e10dd..0000000 --- a/src/createUniform.js +++ /dev/null @@ -1,21 +0,0 @@ -const invariant = require("invariant"); - -module.exports = function (React) { - const { Component, PropTypes } = React; - - class Uniform extends Component { - render () { - invariant( - false, - "GL.Uniform elements are for GL.View configuration only and should not be rendered" - ); - } - } - Uniform.displayName = "GL.Uniform"; - Uniform.propTypes = { - children: PropTypes.any.isRequired, - name: PropTypes.string.isRequired - }; - - return Uniform; -}; diff --git a/src/data/build.js b/src/data/build.js index 6f8353f..29f75da 100644 --- a/src/data/build.js +++ b/src/data/build.js @@ -1,133 +1,108 @@ +const React = require("react"); const invariant = require("invariant"); +const Uniform = require("../Uniform"); +const Shaders = require("../Shaders"); const TextureObjects = require("./TextureObjects"); const isNonSamplerUniformValue = require("./isNonSamplerUniformValue"); +const findGLNodeInGLComponentChildren = require("./findGLNodeInGLComponentChildren"); //// build: converts the VDOM gl-react DSL into an internal data tree. -module.exports = function (React, Shaders, Uniform, GLView) { - // FIXME: maybe with React 0.14, we will be able to make this library depending on React so we don't have to do this closure +module.exports = function build (GLNode, parentWidth, parentHeight, parentPreload, via) { + const props = GLNode.props; + const shader = props.shader; + const GLNodeUniforms = props.uniforms; + const width = props.width || parentWidth; + const height = props.height || parentHeight; + const GLNodeChildren = props.children; + const preload = "preload" in props ? props.preload : preload; - function pickReactFirstChild (children) { - return React.Children.count(children) === 1 ? - (children instanceof Array ? children[0] : children) : - null; - } + invariant(Shaders.exists(shader), "Shader #%s does not exists", shader); - function unfoldGLComponent (c, glComponentNameArray) { - const Class = c.type; - if (!(Class.isGLComponent)) return; - const instance = new Class(); // FIXME: React might eventually improve to ease the work done here. see https://github.com/facebook/react/issues/4697#issuecomment-134335822 - instance.props = c.props; - const child = pickReactFirstChild(instance.render()); - const glComponentName = Class.displayName || Class.name || ""; - glComponentNameArray.push(glComponentName); - return child; - } + const shaderName = Shaders.getName(shader); - function findGLViewInGLComponentChildren (children) { - // going down the VDOM tree, while we can unfold GLComponent - const via = []; - for (let c = children; c && typeof c.type === "function"; c = unfoldGLComponent(c, via)) { - if (c.type === GLView) - return { childGLView: c, via }; // found a GLView - } - } - - return function build (shader, glViewUniforms, width, height, glViewChildren, preload, via) { - invariant(Shaders.exists(shader), "Shader #%s does not exists", shader); - - const shaderName = Shaders.getName(shader); - - const uniforms = { ...glViewUniforms }; - const children = []; - const contents = []; + const uniforms = { ...GLNodeUniforms }; + const children = []; + const contents = []; - React.Children.forEach(glViewChildren, child => { - invariant(child.type === Uniform, "(Shader '%s') GL.View can only contains children of type GL.Uniform. Got '%s'", shaderName, child.type && child.type.displayName || child); - const { name, children, ...opts } = child.props; - invariant(typeof name === "string" && name, "(Shader '%s') GL.Uniform must define an name String", shaderName); - invariant(!glViewUniforms || !(name in glViewUniforms), "(Shader '%s') The uniform '%s' set by GL.Uniform must not be in {uniforms} props", shaderName); - invariant(!(name in uniforms), "(Shader '%s') The uniform '%s' set by GL.Uniform must not be defined in another GL.Uniform", shaderName); - uniforms[name] = !children || children.value ? children : { value: children, opts }; // eslint-disable-line no-undef - }); + React.Children.forEach(GLNodeChildren, child => { + invariant(child.type === Uniform, "(Shader '%s') GL.Node can only contains children of type GL.Uniform. Got '%s'", shaderName, child.type && child.type.displayName || child); + const { name, children, ...opts } = child.props; + invariant(typeof name === "string" && name, "(Shader '%s') GL.Uniform must define an name String", shaderName); + invariant(!GLNodeUniforms || !(name in GLNodeUniforms), "(Shader '%s') The uniform '%s' set by GL.Uniform must not be in {uniforms} props", shaderName); + invariant(!(name in uniforms), "(Shader '%s') The uniform '%s' set by GL.Uniform must not be defined in another GL.Uniform", shaderName); + uniforms[name] = !children || children.value ? children : { value: children, opts }; // eslint-disable-line no-undef + }); - Object.keys(uniforms).forEach(name => { - let value = uniforms[name]; - if (isNonSamplerUniformValue(value)) return; + Object.keys(uniforms).forEach(name => { + let value = uniforms[name]; + if (isNonSamplerUniformValue(value)) return; - let opts, typ = typeof value; + let opts, typ = typeof value; - if (value && typ === "object" && !value.prototype && "value" in value) { - // if value has a value field, we tread this field as the value, but keep opts in memory if provided - if (typeof value.opts === "object") { - opts = value.opts; - } - value = value.value; - typ = typeof value; + if (value && typ === "object" && !value.prototype && "value" in value) { + // if value has a value field, we tread this field as the value, but keep opts in memory if provided + if (typeof value.opts === "object") { + opts = value.opts; } + value = value.value; + typ = typeof value; + } - if (!value) { - // falsy value are accepted to indicate blank texture - uniforms[name] = value; - } - else if (typ === "string") { - // uri specified as a string - uniforms[name] = TextureObjects.withOpts(TextureObjects.URI({ uri: value }), opts); - } - else if (typ === "object" && typeof value.uri === "string") { - // uri specified in an object, we keep all other fields for RN "local" image use-case - uniforms[name] = TextureObjects.withOpts(TextureObjects.URI(value), opts); - } - else if (typ === "object" && value.data && value.shape && value.stride) { - // ndarray kind of texture - uniforms[name] = TextureObjects.withOpts(TextureObjects.NDArray(value), opts); - } - else if(typ === "object" && (value instanceof Array ? React.isValidElement(value[0]) : React.isValidElement(value))) { - // value is a VDOM or array of VDOM - const res = findGLViewInGLComponentChildren(value); - if (res) { - const { childGLView, via } = res; - // We have found a GL.View children, we integrate it in the tree and recursively do the same - const childProps = childGLView.props; - children.push({ - vdom: value, - uniform: name, - data: build( - childProps.shader, - childProps.uniforms, - childProps.width || width, - childProps.height || height, - childProps.children, - "preload" in childProps ? childProps.preload : preload, - via) - }); - } - else { - // in other cases VDOM, we will use child as a content - contents.push({ - vdom: value, - uniform: name, - opts - }); - } + if (!value) { + // falsy value are accepted to indicate blank texture + uniforms[name] = value; + } + else if (typ === "string") { + // uri specified as a string + uniforms[name] = TextureObjects.withOpts(TextureObjects.URI({ uri: value }), opts); + } + else if (typ === "object" && typeof value.uri === "string") { + // uri specified in an object, we keep all other fields for RN "local" image use-case + uniforms[name] = TextureObjects.withOpts(TextureObjects.URI(value), opts); + } + else if (typ === "object" && value.data && value.shape && value.stride) { + // ndarray kind of texture + uniforms[name] = TextureObjects.withOpts(TextureObjects.NDArray(value), opts); + } + else if(typ === "object" && (value instanceof Array ? React.isValidElement(value[0]) : React.isValidElement(value))) { + // value is a VDOM or array of VDOM + const res = findGLNodeInGLComponentChildren(value); + if (res) { + const { childGLNode, via } = res; + // We have found a GL.Node children, we integrate it in the tree and recursively do the same + + children.push({ + vdom: value, + uniform: name, + data: build(childGLNode, width, height, preload, via) + }); } else { - // in any other case, it is an unrecognized invalid format - delete uniforms[name]; - if (typeof console !== "undefined" && console.error) console.error("invalid uniform '"+name+"' value:", value); // eslint-disable-line no-console - invariant(false, "Shader #%s: Unrecognized format for uniform '%s'", shader, name); + // in other cases VDOM, we will use child as a content + contents.push({ + vdom: value, + uniform: name, + opts + }); } - }); + } + else { + // in any other case, it is an unrecognized invalid format + delete uniforms[name]; + if (typeof console !== "undefined" && console.error) console.error("invalid uniform '"+name+"' value:", value); // eslint-disable-line no-console + invariant(false, "Shader #%s: Unrecognized format for uniform '%s'", shader, name); + } + }); - return { - shader, - uniforms, - width, - height, - children, - contents, - preload, - via - }; + return { + shader, + uniforms, + width, + height, + children, + contents, + preload, + via }; }; diff --git a/src/data/findGLNodeInGLComponentChildren.js b/src/data/findGLNodeInGLComponentChildren.js new file mode 100644 index 0000000..0f26a81 --- /dev/null +++ b/src/data/findGLNodeInGLComponentChildren.js @@ -0,0 +1,10 @@ +const unfoldGLComponent = require("./unfoldGLComponent"); + +module.exports = function findGLNodeInGLComponentChildren (children) { + // going down the VDOM tree, while we can unfold GLComponent + const via = []; + for (let c = children; c && typeof c.type === "function"; c = unfoldGLComponent(c, via)) { + if (c.type.isGLNode) + return { childGLNode: c, via }; // found a GLNode + } +}; diff --git a/src/data/index.js b/src/data/index.js index ce98c68..d14946e 100644 --- a/src/data/index.js +++ b/src/data/index.js @@ -1,6 +1,6 @@ module.exports = { - createBuild: require("./build"), + build: require("./build"), fill: require("./fill"), resolve: require("./resolve") }; diff --git a/src/data/pickReactFirstChild.js b/src/data/pickReactFirstChild.js new file mode 100644 index 0000000..82a7334 --- /dev/null +++ b/src/data/pickReactFirstChild.js @@ -0,0 +1,7 @@ +const React = require("react"); + +module.exports = function pickReactFirstChild (children) { + return React.Children.count(children) === 1 ? + (children instanceof Array ? children[0] : children) : + null; +}; diff --git a/src/data/resolve.js b/src/data/resolve.js index 082c083..ddc6854 100644 --- a/src/data/resolve.js +++ b/src/data/resolve.js @@ -7,7 +7,7 @@ const extractImages = require("./extractImages"); const uniqImages = require("./uniqImages"); ///// resolve: takes the output of fill(build(*)) to generate the final data tree -// The algorithm simplifies the data tree to use shared framebuffers if some VDOM is duplicated in the tree (e.g: content / GL.View) +// The algorithm simplifies the data tree to use shared framebuffers if some VDOM is duplicated in the tree (e.g: content / GL.Node) function resolve (dataTree) { let imagesToPreload = []; diff --git a/src/data/unfoldGLComponent.js b/src/data/unfoldGLComponent.js new file mode 100644 index 0000000..af8f312 --- /dev/null +++ b/src/data/unfoldGLComponent.js @@ -0,0 +1,12 @@ +const pickReactFirstChild = require("./pickReactFirstChild"); + +module.exports = function unfoldGLComponent (c, glComponentNameArray) { + const Class = c.type; + if (!(Class.isGLComponent)) return; + const instance = new Class(); // FIXME: React might eventually improve to ease the work done here. see https://github.com/facebook/react/issues/4697#issuecomment-134335822 + instance.props = c.props; + const child = pickReactFirstChild(instance.render()); + const glComponentName = Class.displayName || Class.name || ""; + glComponentNameArray.push(glComponentName); + return child; +}; diff --git a/src/glViewMethods.json b/src/glViewMethods.json deleted file mode 100644 index 287a851..0000000 --- a/src/glViewMethods.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - "getGLCanvas", - "captureFrame" -] diff --git a/src/index.js b/src/index.js index c54e17e..2f96af7 100644 --- a/src/index.js +++ b/src/index.js @@ -1,13 +1,13 @@ const createComponent = require("./createComponent"); -const createComponentDeprecated = require("./createComponentDeprecated"); -const createShaders = require("./createShaders"); -const createUniform = require("./createUniform"); -const createView = require("./createView"); +const createSurface = require("./createSurface"); +const Node = require("./Node"); +const Shaders = require("./Shaders"); +const Uniform = require("./Uniform"); module.exports = { createComponent, - createComponentDeprecated, - createShaders, - createUniform, - createView + createSurface, + Node, + Shaders, + Uniform }; From 698a72d4d6cc28d1eaf4371133723a6659baca99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Fri, 23 Oct 2015 22:11:03 +0200 Subject: [PATCH 2/4] improve invariant message --- src/createComponent.js | 4 ++-- src/createSurface.js | 2 ++ src/data/build.js | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/createComponent.js b/src/createComponent.js index ba5f19d..e36e517 100644 --- a/src/createComponent.js +++ b/src/createComponent.js @@ -11,8 +11,8 @@ module.exports = function createComponent (renderGLNode, staticFields) { const glNode = renderGLNode(this.props); invariant(glNode && glNode.type && (glNode.type.isGLNode || glNode.type.isGLComponent), - "The GL.createComponent function parameter must return a GL.Node or "+ - "another GL Component"); + "%s: The GL.createComponent function parameter must return a GL.Node or "+ + "another GL Component", GLComponent.displayName); return glNode; } diff --git a/src/createSurface.js b/src/createSurface.js index 217adb3..6784b3b 100644 --- a/src/createSurface.js +++ b/src/createSurface.js @@ -50,6 +50,8 @@ module.exports = function (renderVcontainer, renderVcontent, renderVGL) { const { via, childGLNode } = findGLNodeInGLComponentChildren(children); + invariant(childGLNode, "GL.Surface must have in children a GL.Node or a GL Component"); + const { data, contentsVDOM, imagesToPreload } = resolve( fill( diff --git a/src/data/build.js b/src/data/build.js index 29f75da..e70feaa 100644 --- a/src/data/build.js +++ b/src/data/build.js @@ -91,7 +91,7 @@ module.exports = function build (GLNode, parentWidth, parentHeight, parentPreloa // in any other case, it is an unrecognized invalid format delete uniforms[name]; if (typeof console !== "undefined" && console.error) console.error("invalid uniform '"+name+"' value:", value); // eslint-disable-line no-console - invariant(false, "Shader #%s: Unrecognized format for uniform '%s'", shader, name); + invariant(false, "Shader '%s': Unrecognized format for uniform '%s'", shaderName, name); } }); From b3e4d8c1cb2f2e63eaf645f84d4a947ee0bf80c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Mon, 26 Oct 2015 12:39:00 +0100 Subject: [PATCH 3/4] Fix a regression in discovery of shared vdom nodes --- src/data/fill.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/fill.js b/src/data/fill.js index bbd24d9..c25116f 100644 --- a/src/data/fill.js +++ b/src/data/fill.js @@ -3,7 +3,7 @@ function fill (dataTree) { function fillRec (node) { // we compute all the descendants vdom under the current node - let descendantsVDOM = [], descendantsVDOMData = []; + let descendantsVDOM = [ node.vdom ], descendantsVDOMData = [ node.data ]; const newChildren = node.data.children.map(node => { const res = fillRec(node); if (descendantsVDOM.indexOf(res.vdom) === -1) { From e6d1bc13cb05573c0bda105ab6119c936ffc8250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Wed, 2 Dec 2015 15:26:22 +0100 Subject: [PATCH 4/4] don't dep on React directly but via a workaround to support RN --- package.json | 4 ++-- react-native.js | 1 + react.js | 1 + src/Node.js | 2 +- src/Shaders.js | 4 ++-- src/Uniform.js | 2 +- src/createComponent.js | 2 +- src/createSurface.js | 2 +- src/data/build.js | 2 +- src/data/pickReactFirstChild.js | 2 +- src/react-runtime-mutate.js | 13 +++++++++++++ src/react-runtime.js | 19 +++++++++++++++++++ 12 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 react-native.js create mode 100644 react.js create mode 100644 src/react-runtime-mutate.js create mode 100644 src/react-runtime.js diff --git a/package.json b/package.json index e53e4f0..fb04974 100644 --- a/package.json +++ b/package.json @@ -23,12 +23,12 @@ }, "homepage": "https://github.com/ProjectSeptemberInc/gl-react-core#readme", "peerDependencies": { - "react": "^0.14.0" }, "dependencies": { "babelify": "^6.3.0", "envify": "^3.4.0", - "invariant": "^2.1.0" + "invariant": "^2.2.0", + "events": "1.1.0" }, "devDependencies": { "eslint": "^1.6.0", diff --git a/react-native.js b/react-native.js new file mode 100644 index 0000000..db06996 --- /dev/null +++ b/react-native.js @@ -0,0 +1 @@ +require("./src/react-runtime-mutate").set(require("react-native")); diff --git a/react.js b/react.js new file mode 100644 index 0000000..e968ca2 --- /dev/null +++ b/react.js @@ -0,0 +1 @@ +require("./src/react-runtime-mutate").set(require("react")); diff --git a/src/Node.js b/src/Node.js index 828aa5e..190712f 100644 --- a/src/Node.js +++ b/src/Node.js @@ -1,4 +1,4 @@ -const React = require("react"); +const React = require("./react-runtime"); const { Component, PropTypes } = React; const invariant = require("invariant"); diff --git a/src/Shaders.js b/src/Shaders.js index 1864f88..2ca8814 100644 --- a/src/Shaders.js +++ b/src/Shaders.js @@ -1,4 +1,4 @@ -const EventEmitter = require("events"); +const {EventEmitter} = require("events"); const invariant = require("invariant"); let _uid = 1; @@ -35,7 +35,7 @@ const Shaders = { return names[id]; }, list () { - return Object.keys(names); + return Object.keys(shaders); }, exists (id) { return typeof id === "number" && id >= 1 && id < _uid; diff --git a/src/Uniform.js b/src/Uniform.js index 893bb6b..beefb7d 100644 --- a/src/Uniform.js +++ b/src/Uniform.js @@ -1,4 +1,4 @@ -const React = require("react"); +const React = require("./react-runtime"); const { Component, PropTypes } = React; const invariant = require("invariant"); diff --git a/src/createComponent.js b/src/createComponent.js index e36e517..e1fe63d 100644 --- a/src/createComponent.js +++ b/src/createComponent.js @@ -1,4 +1,4 @@ -const React = require("react"); +const React = require("./react-runtime"); const invariant = require("invariant"); module.exports = function createComponent (renderGLNode, staticFields) { diff --git a/src/createSurface.js b/src/createSurface.js index 6784b3b..4c8e2b9 100644 --- a/src/createSurface.js +++ b/src/createSurface.js @@ -1,4 +1,4 @@ -const React = require("react"); +const React = require("./react-runtime"); const { Component, PropTypes diff --git a/src/data/build.js b/src/data/build.js index e70feaa..2e4b904 100644 --- a/src/data/build.js +++ b/src/data/build.js @@ -1,4 +1,4 @@ -const React = require("react"); +const React = require("../react-runtime"); const invariant = require("invariant"); const Uniform = require("../Uniform"); const Shaders = require("../Shaders"); diff --git a/src/data/pickReactFirstChild.js b/src/data/pickReactFirstChild.js index 82a7334..ebe1dfc 100644 --- a/src/data/pickReactFirstChild.js +++ b/src/data/pickReactFirstChild.js @@ -1,4 +1,4 @@ -const React = require("react"); +const React = require("../react-runtime"); module.exports = function pickReactFirstChild (children) { return React.Children.count(children) === 1 ? diff --git a/src/react-runtime-mutate.js b/src/react-runtime-mutate.js new file mode 100644 index 0000000..695342f --- /dev/null +++ b/src/react-runtime-mutate.js @@ -0,0 +1,13 @@ +const invariant = require("invariant"); +let runtime; +function set (React) { + if (React.version) { // RN don't provide version... + const version = React.version.split("."); + invariant(version[0]==="0" && parseInt(version[1], 10) >= 14, "React version must be at least 0.14.x. got: %s", React.version); + } + runtime = React; +} +module.exports = { + set, + get: () => runtime +}; diff --git a/src/react-runtime.js b/src/react-runtime.js new file mode 100644 index 0000000..846b99e --- /dev/null +++ b/src/react-runtime.js @@ -0,0 +1,19 @@ +const runtime = require("./react-runtime-mutate").get(); +if (!runtime) { + console.warn( // eslint-disable-line no-console +`Please prepend in your JavaScript entry point one of following imports: + +${"require"}("gl-react/react") // for React + + OR + +${"require"}("gl-react/react-native") // for React Native + + +Make sure to do this BEFORE any other imports. (that are gl-react related) + +> Note: This mechanism will be removed once React Native depends on React`); + + throw new Error("gl-react: React instance not available at runtime. Please read instructions"); +} +module.exports = runtime;