Преглед изворни кода

v0.1.0-alpha.12

test: add builder test cases

refactor: split next 0.x
janryWang пре 7 година
родитељ
комит
ed015137a1
64 измењених фајлова са 2477 додато и 252 уклоњено
  1. 2 1
      .babelrc
  2. 4 0
      jest.config.js
  3. 1 1
      lerna.json
  4. 1 0
      package.json
  5. 2 2
      packages/antd/package.json
  6. 9 0
      packages/builder/.babelrc
  7. 4 0
      packages/builder/.gitignore
  8. 5 1
      packages/builder/.npmignore
  9. 91 0
      packages/builder/config/env.js
  10. 12 0
      packages/builder/config/jest/cssTransform.js
  11. 10 0
      packages/builder/config/jest/fileTransform.js
  12. 62 0
      packages/builder/config/paths.js
  13. 20 0
      packages/builder/config/polyfills.js
  14. 4 0
      packages/builder/config/setupTests.js
  15. 247 0
      packages/builder/config/webpack.config.dev.js
  16. 98 0
      packages/builder/config/webpackDevServer.config.js
  17. 463 0
      packages/builder/package-lock.json
  18. 40 10
      packages/builder/package.json
  19. BIN
      packages/builder/public/favicon.ico
  20. 40 0
      packages/builder/public/index.html
  21. 15 0
      packages/builder/public/manifest.json
  22. 90 0
      packages/builder/scripts/start.js
  23. 4 9
      packages/builder/src/App.js
  24. 26 0
      packages/builder/src/__tests__/__snapshots__/comp.js.snap
  25. 26 0
      packages/builder/src/__tests__/__snapshots__/comp.spec.js.snap
  26. 146 0
      packages/builder/src/__tests__/actions/index.spec.js
  27. 8 0
      packages/builder/src/__tests__/baseForm.spec.js
  28. 22 0
      packages/builder/src/__tests__/comp.spec.js
  29. 15 0
      packages/builder/src/__tests__/index.spec.js
  30. 48 2
      packages/builder/src/__tests__/lang.spec.js
  31. 702 0
      packages/builder/src/__tests__/reducers/index.spec.js
  32. 6 15
      packages/builder/src/components/editor/index.js
  33. 3 1
      packages/builder/src/components/editor/style.js
  34. 4 9
      packages/builder/src/components/fields/index.js
  35. 5 9
      packages/builder/src/components/fields/layout.js
  36. 4 2
      packages/builder/src/components/fields/style.js
  37. 7 4
      packages/builder/src/components/globalBtnList/index.js
  38. 18 3
      packages/builder/src/components/preview/fieldMiddleware.js
  39. 5 9
      packages/builder/src/components/preview/index.js
  40. 3 1
      packages/builder/src/components/preview/style.js
  41. 24 14
      packages/builder/src/components/props/accordList.js
  42. 2 1
      packages/builder/src/components/props/defaultValueCascader/index.js
  43. 8 4
      packages/builder/src/components/props/editors/fieldAttrEditors/dataSourceEditor.js
  44. 10 9
      packages/builder/src/components/props/editors/fieldAttrEditors/dataSourceEnum.js
  45. 2 3
      packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/arrayDefaultEditor.js
  46. 1 2
      packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/boolDefaultEditor.js
  47. 4 4
      packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/dateDefaultEditor.js
  48. 1 2
      packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/dateTimeDefaultEditor.js
  49. 2 3
      packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/defaultValueGenerator.js
  50. 6 5
      packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/index.js
  51. 8 8
      packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/monthDefaultEditor.js
  52. 3 2
      packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/stringDefaultEditor.js
  53. 5 8
      packages/builder/src/components/props/fileSetting.js
  54. 10 11
      packages/builder/src/components/props/propsSetting.js
  55. 3 1
      packages/builder/src/components/props/style.js
  56. 100 0
      packages/builder/src/demo/index.js
  57. 0 2
      packages/builder/src/reducers/componentId.js
  58. 4 0
      packages/builder/src/reducers/initSchemaData.js
  59. 7 6
      packages/builder/src/style.js
  60. 0 60
      packages/builder/src/utils/comp.js
  61. 0 23
      packages/builder/src/utils/lang.js
  62. 1 1
      packages/core/package.json
  63. 2 2
      packages/next/package.json
  64. 2 2
      packages/react/package.json

+ 2 - 1
.babelrc

@@ -4,6 +4,7 @@
     "@babel/plugin-transform-runtime",
     "@babel/plugin-proposal-class-properties",
     "@babel/plugin-syntax-dynamic-import",
-    ["@babel/plugin-proposal-decorators", { "legacy": true }]
+    ["@babel/plugin-proposal-decorators", { "legacy": true }],
+    "babel-plugin-dynamic-import-node"
   ]
 }

+ 4 - 0
jest.config.js

@@ -18,6 +18,10 @@ module.exports = {
   coveragePathIgnorePatterns: [
     '/node_modules/',
     'package.json',
+    '/demo/',
+    '/packages/builder/src/__tests__/',
+    '/packages/builder/src/components/',
+    '/packages/builder/src/configs/',
     'package-lock.json'
   ]
 }

+ 1 - 1
lerna.json

@@ -2,7 +2,7 @@
   "packages": [
     "packages/*"
   ],
-  "version": "0.1.0-alpha.11",
+  "version": "0.1.0-alpha.12",
   "command": {
     "bootstrap": {
       "npmClientArgs": [

+ 1 - 0
package.json

@@ -35,6 +35,7 @@
     "babel-eslint": "^10.0.1",
     "babel-jest": "^24.1.0",
     "babel-loader": "^8.0.4",
+    "babel-plugin-dynamic-import-node": "^2.2.0",
     "chalk": "^2.4.2",
     "chokidar": "^2.1.2",
     "concurrently": "^4.1.0",

+ 2 - 2
packages/antd/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@uform/antd",
-  "version": "0.1.0-alpha.11",
+  "version": "0.1.0-alpha.12",
   "license": "MIT",
   "main": "lib",
   "repository": {
@@ -19,7 +19,7 @@
     "react-dom": ">=16.8.0"
   },
   "dependencies": {
-    "@uform/react": "^0.1.0-alpha.11",
+    "@uform/react": "^0.1.0-alpha.12",
     "@uform/utils": "^0.1.0-alpha.10",
     "classnames": "^2.2.6",
     "moveto": "^1.7.4",

+ 9 - 0
packages/builder/.babelrc

@@ -0,0 +1,9 @@
+{
+  "presets": [["@babel/preset-env", { "loose": true }], "@babel/preset-react"],
+  "plugins": [
+    "@babel/plugin-transform-runtime",
+    "@babel/plugin-proposal-class-properties",
+    "@babel/plugin-syntax-dynamic-import",
+    ["@babel/plugin-proposal-decorators", { "legacy": true }]
+  ]
+}

+ 4 - 0
packages/builder/.gitignore

@@ -0,0 +1,4 @@
+config
+public
+scripts
+src/demo

+ 5 - 1
packages/builder/.npmignore

@@ -1,2 +1,6 @@
 node_modules
-*.log
+config
+public
+scripts
+*.log
+src/demo

+ 91 - 0
packages/builder/config/env.js

@@ -0,0 +1,91 @@
+const fs = require('fs')
+const path = require('path')
+const paths = require('./paths')
+
+// Make sure that including paths.js after env.js will read .env variables.
+delete require.cache[require.resolve('./paths')]
+
+const { NODE_ENV } = process.env
+if (!NODE_ENV) {
+  throw new Error(
+    'The NODE_ENV environment variable is required but was not specified.'
+  )
+}
+
+// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
+const dotenvFiles = [
+  `${paths.dotenv}.${NODE_ENV}.local`,
+  `${paths.dotenv}.${NODE_ENV}`,
+  // Don't include `.env.local` for `test` environment
+  // since normally you expect tests to produce the same
+  // results for everyone
+  NODE_ENV !== 'test' && `${paths.dotenv}.local`,
+  paths.dotenv
+].filter(Boolean)
+
+// Load environment variables from .env* files. Suppress warnings using silent
+// if this file is missing. dotenv will never modify any environment variables
+// that have already been set.  Variable expansion is supported in .env files.
+// https://github.com/motdotla/dotenv
+// https://github.com/motdotla/dotenv-expand
+dotenvFiles.forEach(dotenvFile => {
+  if (fs.existsSync(dotenvFile)) {
+    require('dotenv-expand')(
+      require('dotenv').config({
+        path: dotenvFile
+      })
+    )
+  }
+})
+
+// We support resolving modules according to `NODE_PATH`.
+// This lets you use absolute paths in imports inside large monorepos:
+// https://github.com/facebookincubator/create-react-app/issues/253.
+// It works similar to `NODE_PATH` in Node itself:
+// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
+// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
+// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
+// https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
+// We also resolve them to make sure all tools using them work consistently.
+const appDirectory = fs.realpathSync(process.cwd())
+process.env.NODE_PATH = (process.env.NODE_PATH || '')
+  .split(path.delimiter)
+  .filter(folder => folder && !path.isAbsolute(folder))
+  .map(folder => path.resolve(appDirectory, folder))
+  .join(path.delimiter)
+
+// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
+// injected into the application via DefinePlugin in Webpack configuration.
+const REACT_APP = /^REACT_APP_/i
+
+function getClientEnvironment(publicUrl) {
+  const raw = Object.keys(process.env)
+    .filter(key => REACT_APP.test(key))
+    .reduce(
+      (env, key) => {
+        env[key] = process.env[key]
+        return env
+      },
+      {
+        // Useful for determining whether we’re running in production mode.
+        // Most importantly, it switches React into the correct mode.
+        NODE_ENV: process.env.NODE_ENV || 'development',
+        // Useful for resolving the correct path to static assets in `public`.
+        // For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
+        // This should only be used as an escape hatch. Normally you would put
+        // images into the `src` and `import` them in code to get their paths.
+        PUBLIC_URL: publicUrl
+      }
+    )
+  // Stringify all values so we can feed into Webpack DefinePlugin
+  const stringified = {
+    'process.env': Object.keys(raw).reduce((env, key) => {
+      env[key] = JSON.stringify(raw[key])
+      return env
+    }, {})
+  }
+
+  return { raw, stringified }
+}
+
+module.exports = getClientEnvironment

+ 12 - 0
packages/builder/config/jest/cssTransform.js

@@ -0,0 +1,12 @@
+// This is a custom Jest transformer turning style imports into empty objects.
+// http://facebook.github.io/jest/docs/en/webpack.html
+
+module.exports = {
+  process() {
+    return 'module.exports = {};'
+  },
+  getCacheKey() {
+    // The output is always the same.
+    return 'cssTransform'
+  }
+}

+ 10 - 0
packages/builder/config/jest/fileTransform.js

@@ -0,0 +1,10 @@
+const path = require('path')
+
+// This is a custom Jest transformer turning file imports into filenames.
+// http://facebook.github.io/jest/docs/en/webpack.html
+
+module.exports = {
+  process(src, filename) {
+    return `module.exports = ${JSON.stringify(path.basename(filename))};`
+  }
+}

+ 62 - 0
packages/builder/config/paths.js

@@ -0,0 +1,62 @@
+const path = require('path')
+const fs = require('fs')
+const url = require('url')
+
+// Make sure any symlinks in the project folder are resolved:
+// https://github.com/facebookincubator/create-react-app/issues/637
+const appDirectory = fs.realpathSync(process.cwd())
+const resolveApp = relativePath => path.resolve(appDirectory, relativePath)
+
+const envPublicUrl = process.env.PUBLIC_URL
+
+function ensureSlash(path, needsSlash) {
+  const hasSlash = path.endsWith('/')
+  if (hasSlash && !needsSlash) {
+    return path.substr(path, path.length - 1)
+  } else if (!hasSlash && needsSlash) {
+    return `${path}/`
+  } else {
+    return path
+  }
+}
+
+const getPublicUrl = appPackageJson =>
+  envPublicUrl || require(appPackageJson).homepage
+
+// We use `PUBLIC_URL` environment variable or "homepage" field to infer
+// "public path" at which the app is served.
+// Webpack needs to know it to put the right <script> hrefs into HTML even in
+// single-page apps that may serve index.html for nested URLs like /todos/42.
+// We can't use a relative path in HTML because we don't want to load something
+// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
+function getServedPath(appPackageJson) {
+  const publicUrl = getPublicUrl(appPackageJson)
+  const servedUrl =
+    envPublicUrl || (publicUrl ? url.parse(publicUrl).pathname : '/')
+  return ensureSlash(servedUrl, true)
+}
+
+// config after eject: we're in ./config/
+module.exports = {
+  dotenv: resolveApp('.env'),
+  appBuild: resolveApp('build'),
+  appDemoBuild: resolveApp('demo'),
+  appPublic: resolveApp('public'),
+  appHtml: resolveApp('public/index.html'),
+  appIndexJs: resolveApp('src/demo/index.js'), // CRL: Updated for demo purposes
+  appPackageJson: resolveApp('package.json'),
+  appSrc: resolveApp('src'),
+  yarnLockFile: resolveApp('yarn.lock'),
+  testsSetup: resolveApp('src/setupTests.js'),
+  appNodeModules: resolveApp('node_modules'),
+  publicUrl: getPublicUrl(resolveApp('package.json')),
+  servedPath: getServedPath(resolveApp('package.json')),
+
+  // CRL: New paths for demo build
+  appDemoIndexJs: resolveApp('src/demo/index.js'),
+  appDemoSrc: resolveApp('src/demo'),
+
+  // CRL: New paths for library
+  appLibIndexJs: resolveApp('src/index.js'),
+  appLibSrc: resolveApp('src')
+}

+ 20 - 0
packages/builder/config/polyfills.js

@@ -0,0 +1,20 @@
+if (typeof Promise === 'undefined') {
+  // Rejection tracking prevents a common issue where React gets into an
+  // inconsistent state due to an error, but it gets swallowed by a Promise,
+  // and the user has no idea what causes React's erratic future behavior.
+  // require('promise/lib/rejection-tracking').enable();
+  // window.Promise = require('promise/lib/es6-extensions.js');
+}
+
+// fetch() polyfill for making API calls.
+// require('whatwg-fetch');
+
+// Object.assign() is commonly used with React.
+// It will use the native implementation if it's present and isn't buggy.
+// Object.assign = require('object-assign');
+
+// In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet.
+// We don't polyfill it in the browser--this is user's responsibility.
+if (process.env.NODE_ENV === 'test') {
+  require('raf').polyfill(global)
+}

+ 4 - 0
packages/builder/config/setupTests.js

@@ -0,0 +1,4 @@
+import { configure } from 'enzyme'
+import Adapter from 'enzyme-adapter-react-16'
+
+configure({ adapter: new Adapter() })

+ 247 - 0
packages/builder/config/webpack.config.dev.js

@@ -0,0 +1,247 @@
+const autoprefixer = require('autoprefixer')
+const path = require('path')
+const webpack = require('webpack')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')
+const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin')
+const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin')
+const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin')
+const getClientEnvironment = require('./env')
+const paths = require('./paths')
+
+// Webpack uses `publicPath` to determine where the app is being served from.
+// In development, we always serve from the root. This makes config easier.
+const publicPath = '/'
+// `publicUrl` is just like `publicPath`, but we will provide it to our app
+// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
+// Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
+const publicUrl = ''
+// Get environment variables to inject into our app.
+const env = getClientEnvironment(publicUrl)
+
+// This is the development configuration.
+// It is focused on developer experience and fast rebuilds.
+// The production configuration is different and lives in a separate file.
+module.exports = {
+  // You may want 'eval' instead if you prefer to see the compiled output in DevTools.
+  // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.
+  devtool: 'cheap-module-source-map',
+  // These are the "entry points" to our application.
+  // This means they will be the "root" imports that are included in JS bundle.
+  // The first two entry points enable "hot" CSS and auto-refreshes for JS.
+  entry: [
+    // Include an alternative client for WebpackDevServer. A client's job is to
+    // connect to WebpackDevServer by a socket and get notified about changes.
+    // When you save a file, the client will either apply hot updates (in case
+    // of CSS changes), or refresh the page (in case of JS changes). When you
+    // make a syntax error, this client will display a syntax error overlay.
+    // Note: instead of the default WebpackDevServer client, we use a custom one
+    // to bring better experience for Create React App users. You can replace
+    // the line below with these two lines if you prefer the stock client:
+    // require.resolve('webpack-dev-server/client') + '?/',
+    // require.resolve('webpack/hot/dev-server'),
+    require.resolve('react-dev-utils/webpackHotDevClient'),
+    // Finally, this is your app's code:
+    paths.appIndexJs
+    // We include the app code last so that if there is a runtime error during
+    // initialization, it doesn't blow up the WebpackDevServer client, and
+    // changing JS code would still trigger a refresh.
+  ],
+  output: {
+    // Add /* filename */ comments to generated require()s in the output.
+    pathinfo: true,
+    // This does not produce a real file. It's just the virtual path that is
+    // served by WebpackDevServer in development. This is the JS bundle
+    // containing code from all our entry points, and the Webpack runtime.
+    filename: 'static/js/bundle.js',
+    // There are also additional JS chunk files if you use code splitting.
+    chunkFilename: 'static/js/[name].chunk.js',
+    // This is the URL that app is served from. We use "/" in development.
+    publicPath,
+    // Point sourcemap entries to original disk location (format as URL on Windows)
+    devtoolModuleFilenameTemplate: info =>
+      path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')
+  },
+  resolve: {
+    // This allows you to set a fallback for where Webpack should look for modules.
+    // We placed these paths second because we want `node_modules` to "win"
+    // if there are any conflicts. This matches Node resolution mechanism.
+    // https://github.com/facebookincubator/create-react-app/issues/253
+    modules: ['node_modules', paths.appNodeModules].concat(
+      // It is guaranteed to exist because we tweak it in `env.js`
+      process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
+    ),
+    // These are the reasonable defaults supported by the Node ecosystem.
+    // We also include JSX as a common component filename extension to support
+    // some tools, although we do not recommend using it, see:
+    // https://github.com/facebookincubator/create-react-app/issues/290
+    // `web` extension prefixes have been added for better support
+    // for React Native Web.
+    extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'],
+    alias: {
+      // Support React Native Web
+      // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
+      'react-native': 'react-native-web'
+    },
+    plugins: [
+      // Prevents users from importing files from outside of src/ (or node_modules/).
+      // This often causes confusion because we only process files within src/ with babel.
+      // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
+      // please link the files into your node_modules/ and let module-resolution kick in.
+      // Make sure your source files are compiled, as they will not be processed in any way.
+      new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson])
+    ]
+  },
+  module: {
+    strictExportPresence: true,
+    rules: [
+      // TODO: Disable require.ensure as it's not a standard language feature.
+      // We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176.
+      // { parser: { requireEnsure: false } },
+
+      // First, run the linter.
+      // It's important to do this before Babel processes the JS.
+      {
+        // "oneOf" will traverse all following loaders until one will
+        // match the requirements. When no loader matches it will fall
+        // back to the "file" loader at the end of the loader list.
+        oneOf: [
+          // "url" loader works like "file" loader except that it embeds assets
+          // smaller than specified limit in bytes as data URLs to avoid requests.
+          // A missing `test` is equivalent to a match.
+          {
+            test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
+            loader: require.resolve('url-loader'),
+            options: {
+              limit: 10000,
+              name: '[name].[ext]',
+              outputPath: 'static/media/',
+              publicPath: '../'
+            }
+          },
+          // Process JS with Babel.
+          {
+            test: /\.(js|jsx|mjs)$/,
+            include: paths.appSrc,
+            loader: require.resolve('babel-loader'),
+            options: {
+              // This is a feature of `babel-loader` for webpack (not Babel itself).
+              // It enables caching results in ./node_modules/.cache/babel-loader/
+              // directory for faster rebuilds.
+              cacheDirectory: true
+            }
+          },
+          // "postcss" loader applies autoprefixer to our CSS.
+          // "css" loader resolves paths in CSS and adds assets as dependencies.
+          // "style" loader turns CSS into JS modules that inject <style> tags.
+          // In production, we use a plugin to extract that CSS to a file, but
+          // in development "style" loader enables hot editing of CSS.
+          {
+            test: /\.(css|less)$/,
+            use: [
+              require.resolve('style-loader'),
+              {
+                loader: require.resolve('css-loader'),
+                options: {
+                  importLoaders: 1
+                }
+              },
+              {
+                loader: require.resolve('postcss-loader'),
+                options: {
+                  // Necessary for external CSS imports to work
+                  // https://github.com/facebookincubator/create-react-app/issues/2677
+                  ident: 'postcss',
+                  plugins: () => [
+                    require('postcss-flexbugs-fixes'),
+                    autoprefixer({
+                      browsers: [
+                        '>1%',
+                        'last 4 versions',
+                        'Firefox ESR',
+                        'not ie < 9' // React doesn't support IE8 anyway
+                      ],
+                      flexbox: 'no-2009'
+                    })
+                  ]
+                }
+              },
+              {
+                loader: require.resolve('less-loader') // compiles Less to CSS
+              }
+            ]
+          },
+          // "file" loader makes sure those assets get served by WebpackDevServer.
+          // When you `import` an asset, you get its (virtual) filename.
+          // In production, they would get copied to the `build` folder.
+          // This loader doesn't use a "test" so it will catch all modules
+          // that fall through the other loaders.
+          {
+            // Exclude `js` files to keep "css" loader working as it injects
+            // it's runtime that would otherwise processed through "file" loader.
+            // Also exclude `html` and `json` extensions so they get processed
+            // by webpacks internal loaders.
+            exclude: [/\.(js|jsx|mjs)$/, /\.(less|css)$/, /\.html$/, /\.json$/],
+            loader: require.resolve('file-loader'),
+            options: {
+              name: '[name].[hash:8].[ext]',
+              outputPath: 'static/media/',
+              publicPath: '../'
+            }
+          }
+        ]
+      }
+      // ** STOP ** Are you adding a new loader?
+      // Make sure to add the new loader(s) before the "file" loader.
+    ]
+  },
+  plugins: [
+    // Makes some environment variables available in index.html.
+    // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
+    // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
+    // In development, this will be an empty string.
+    new InterpolateHtmlPlugin(env.raw),
+    // Generates an `index.html` file with the <script> injected.
+    new HtmlWebpackPlugin({
+      inject: true,
+      template: paths.appHtml
+    }),
+    // Add module names to factory functions so they appear in browser profiler.
+    new webpack.NamedModulesPlugin(),
+    // Makes some environment variables available to the JS code, for example:
+    // if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`.
+    new webpack.DefinePlugin(env.stringified),
+    // This is necessary to emit hot updates (currently CSS only):
+    new webpack.HotModuleReplacementPlugin(),
+    // Watcher doesn't work well if you mistype casing in a path so we use
+    // a plugin that prints an error when you attempt to do this.
+    // See https://github.com/facebookincubator/create-react-app/issues/240
+    new CaseSensitivePathsPlugin(),
+    // If you require a missing module and then `npm install` it, you still have
+    // to restart the development server for Webpack to discover it. This plugin
+    // makes the discovery automatic so you don't have to restart.
+    // See https://github.com/facebookincubator/create-react-app/issues/186
+    new WatchMissingNodeModulesPlugin(paths.appNodeModules),
+    // Moment.js is an extremely popular library that bundles large locale files
+    // by default due to how Webpack interprets its code. This is a practical
+    // solution that requires the user to opt into importing specific locales.
+    // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
+    // You can remove this if you don't use Moment.js:
+    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
+  ],
+  // Some libraries import Node modules but don't use them in the browser.
+  // Tell Webpack to provide empty mocks for them so importing them works.
+  node: {
+    dgram: 'empty',
+    fs: 'empty',
+    net: 'empty',
+    tls: 'empty',
+    child_process: 'empty'
+  },
+  // Turn off performance hints during development because we don't do any
+  // splitting or minification in interest of speed. These warnings become
+  // cumbersome.
+  performance: {
+    hints: false
+  }
+}

+ 98 - 0
packages/builder/config/webpackDevServer.config.js

@@ -0,0 +1,98 @@
+const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware')
+const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware')
+const path = require('path')
+const config = require('./webpack.config.dev')
+const paths = require('./paths')
+
+const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'
+const host = process.env.HOST || '0.0.0.0'
+
+module.exports = function(proxy, allowedHost) {
+  return {
+    // WebpackDevServer 2.4.3 introduced a security fix that prevents remote
+    // websites from potentially accessing local content through DNS rebinding:
+    // https://github.com/webpack/webpack-dev-server/issues/887
+    // https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
+    // However, it made several existing use cases such as development in cloud
+    // environment or subdomains in development significantly more complicated:
+    // https://github.com/facebookincubator/create-react-app/issues/2271
+    // https://github.com/facebookincubator/create-react-app/issues/2233
+    // While we're investigating better solutions, for now we will take a
+    // compromise. Since our WDS configuration only serves files in the `public`
+    // folder we won't consider accessing them a vulnerability. However, if you
+    // use the `proxy` feature, it gets more dangerous because it can expose
+    // remote code execution vulnerabilities in backends like Django and Rails.
+    // So we will disable the host check normally, but enable it if you have
+    // specified the `proxy` setting. Finally, we let you override it if you
+    // really know what you're doing with a special environment variable.
+    disableHostCheck:
+      !proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
+    // Enable gzip compression of generated files.
+    compress: true,
+    // Silence WebpackDevServer's own logs since they're generally not useful.
+    // It will still show compile warnings and errors with this setting.
+    clientLogLevel: 'none',
+    // By default WebpackDevServer serves physical files from current directory
+    // in addition to all the virtual build products that it serves from memory.
+    // This is confusing because those files won’t automatically be available in
+    // production build folder unless we copy them. However, copying the whole
+    // project directory is dangerous because we may expose sensitive files.
+    // Instead, we establish a convention that only files in `public` directory
+    // get served. Our build script will copy `public` into the `build` folder.
+    // In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
+    // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
+    // In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
+    // Note that we only recommend to use `public` folder as an escape hatch
+    // for files like `favicon.ico`, `manifest.json`, and libraries that are
+    // for some reason broken when imported through Webpack. If you just want to
+    // use an image, put it in `src` and `import` it from JavaScript instead.
+    contentBase: paths.appPublic,
+    // By default files from `contentBase` will not trigger a page reload.
+    watchContentBase: true,
+    // Enable hot reloading server. It will provide /sockjs-node/ endpoint
+    // for the WebpackDevServer client so it can learn when the files were
+    // updated. The WebpackDevServer client is included as an entry point
+    // in the Webpack development configuration. Note that only changes
+    // to CSS are currently hot reloaded. JS changes will refresh the browser.
+    hot: true,
+    // It is important to tell WebpackDevServer to use the same "root" path
+    // as we specified in the config. In development, we always serve from /.
+    publicPath: config.output.publicPath,
+    // WebpackDevServer is noisy by default so we emit custom message instead
+    // by listening to the compiler events with `compiler.plugin` calls above.
+    quiet: true,
+    // Reportedly, this avoids CPU overload on some systems.
+    // https://github.com/facebookincubator/create-react-app/issues/293
+    // src/node_modules is not ignored to support absolute imports
+    // https://github.com/facebookincubator/create-react-app/issues/1065
+    watchOptions: {
+      ignored: new RegExp(
+        `^(?!${path
+          .normalize(`${paths.appSrc}/`)
+          .replace(/[\\]+/g, '\\\\')}).+[\\\\/]node_modules[\\\\/]`,
+        'g'
+      )
+    },
+    // Enable HTTPS if the HTTPS environment variable is set to 'true'
+    https: protocol === 'https',
+    host,
+    overlay: false,
+    historyApiFallback: {
+      // Paths with dots should still use the history fallback.
+      // See https://github.com/facebookincubator/create-react-app/issues/387.
+      disableDotRule: true
+    },
+    public: allowedHost,
+    proxy,
+    before(app) {
+      // This lets us open files from the runtime error overlay.
+      app.use(errorOverlayMiddleware())
+      // This service worker file is effectively a 'no-op' that will reset any
+      // previous service worker registered for the same host:port combination.
+      // We do this in development to avoid hitting the production cache if
+      // it used the same host and port.
+      // https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432
+      app.use(noopServiceWorkerMiddleware())
+    }
+  }
+}

+ 463 - 0
packages/builder/package-lock.json

@@ -0,0 +1,463 @@
+{
+	"name": "@uform/builder",
+	"version": "0.1.0-alpha.3",
+	"lockfileVersion": 1,
+	"requires": true,
+	"dependencies": {
+		"@alifd/next": {
+			"version": "1.13.2",
+			"resolved": "http://registry.npm.alibaba-inc.com/@alifd/next/download/@alifd/next-1.13.2.tgz",
+			"integrity": "sha1-4yPxdLeRUuxkE9JRiHofZhqFxoE=",
+			"requires": {
+				"babel-runtime": "^6.26.0",
+				"classnames": "^2.2.3",
+				"hoist-non-react-statics": "^2.1.0",
+				"prop-types": "^15.6.0",
+				"react-transition-group": "^2.2.1",
+				"shallow-element-equals": "^1.0.1"
+			}
+		},
+		"@babel/helper-annotate-as-pure": {
+			"version": "7.0.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/@babel/helper-annotate-as-pure/download/@babel/helper-annotate-as-pure-7.0.0.tgz",
+			"integrity": "sha1-Mj053QtQ4Qx8Bsp9djjmhk2MXDI=",
+			"requires": {
+				"@babel/types": "^7.0.0"
+			}
+		},
+		"@babel/helper-module-imports": {
+			"version": "7.0.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/@babel/helper-module-imports/download/@babel/helper-module-imports-7.0.0.tgz",
+			"integrity": "sha1-lggbcRHkhtpNLNlxrRpP4hbMLj0=",
+			"requires": {
+				"@babel/types": "^7.0.0"
+			}
+		},
+		"@babel/runtime": {
+			"version": "7.3.1",
+			"resolved": "http://registry.npm.alibaba-inc.com/@babel/runtime/download/@babel/runtime-7.3.1.tgz",
+			"integrity": "sha1-V0sD6OipiY6vSocqkuogt4Rvbyo=",
+			"requires": {
+				"regenerator-runtime": "^0.12.0"
+			},
+			"dependencies": {
+				"regenerator-runtime": {
+					"version": "0.12.1",
+					"resolved": "http://registry.npm.alibaba-inc.com/regenerator-runtime/download/regenerator-runtime-0.12.1.tgz",
+					"integrity": "sha1-+hpxVEdkwDb4xJsToIsllMn4oN4="
+				}
+			}
+		},
+		"@babel/types": {
+			"version": "7.3.3",
+			"resolved": "http://registry.npm.alibaba-inc.com/@babel/types/download/@babel/types-7.3.3.tgz",
+			"integrity": "sha1-bETRzawqdiW2JCFmV9W8bBB6tDY=",
+			"requires": {
+				"esutils": "^2.0.2",
+				"lodash": "^4.17.11",
+				"to-fast-properties": "^2.0.0"
+			}
+		},
+		"@emotion/is-prop-valid": {
+			"version": "0.7.3",
+			"resolved": "http://registry.npm.alibaba-inc.com/@emotion/is-prop-valid/download/@emotion/is-prop-valid-0.7.3.tgz",
+			"integrity": "sha1-pr9PpTh8u6WdROaYpGgPSBqNpsw=",
+			"requires": {
+				"@emotion/memoize": "0.7.1"
+			}
+		},
+		"@emotion/memoize": {
+			"version": "0.7.1",
+			"resolved": "http://registry.npm.alibaba-inc.com/@emotion/memoize/download/@emotion/memoize-0.7.1.tgz",
+			"integrity": "sha1-6TwTlCWSz17wGqgpdETcGSvu5S8="
+		},
+		"@emotion/unitless": {
+			"version": "0.7.3",
+			"resolved": "http://registry.npm.alibaba-inc.com/@emotion/unitless/download/@emotion/unitless-0.7.3.tgz",
+			"integrity": "sha1-YxCgR/EtIaEDb7AxMXIZiSRAQW8="
+		},
+		"babel-plugin-styled-components": {
+			"version": "1.10.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/babel-plugin-styled-components/download/babel-plugin-styled-components-1.10.0.tgz",
+			"integrity": "sha1-/x9CrSzHjCHya2Ima49WTbyGKTk=",
+			"requires": {
+				"@babel/helper-annotate-as-pure": "^7.0.0",
+				"@babel/helper-module-imports": "^7.0.0",
+				"babel-plugin-syntax-jsx": "^6.18.0",
+				"lodash": "^4.17.10"
+			}
+		},
+		"babel-plugin-syntax-jsx": {
+			"version": "6.18.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/babel-plugin-syntax-jsx/download/babel-plugin-syntax-jsx-6.18.0.tgz",
+			"integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
+		},
+		"babel-runtime": {
+			"version": "6.26.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/babel-runtime/download/babel-runtime-6.26.0.tgz",
+			"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+			"requires": {
+				"core-js": "^2.4.0",
+				"regenerator-runtime": "^0.11.0"
+			}
+		},
+		"camel-case": {
+			"version": "3.0.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/camel-case/download/camel-case-3.0.0.tgz",
+			"integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
+			"requires": {
+				"no-case": "^2.2.0",
+				"upper-case": "^1.1.1"
+			}
+		},
+		"camelize": {
+			"version": "1.0.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/camelize/download/camelize-1.0.0.tgz",
+			"integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
+		},
+		"classnames": {
+			"version": "2.2.6",
+			"resolved": "http://registry.npm.alibaba-inc.com/classnames/download/classnames-2.2.6.tgz",
+			"integrity": "sha1-Q5Nb/90pHzJtrQogUwmzjQD2UM4="
+		},
+		"core-js": {
+			"version": "2.6.5",
+			"resolved": "http://registry.npm.alibaba-inc.com/core-js/download/core-js-2.6.5.tgz",
+			"integrity": "sha1-RLyNJJ5/sv9dAOA0Gn/7lPv2eJU="
+		},
+		"css-color-keywords": {
+			"version": "1.0.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/css-color-keywords/download/css-color-keywords-1.0.0.tgz",
+			"integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU="
+		},
+		"css-to-react-native": {
+			"version": "2.3.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/css-to-react-native/download/css-to-react-native-2.3.0.tgz",
+			"integrity": "sha1-v4DSTsSgjkMDBu9CnAWG5u1Uhfc=",
+			"requires": {
+				"camelize": "^1.0.0",
+				"css-color-keywords": "^1.0.0",
+				"postcss-value-parser": "^3.3.0"
+			}
+		},
+		"deep-diff": {
+			"version": "0.3.8",
+			"resolved": "http://registry.npm.alibaba-inc.com/deep-diff/download/deep-diff-0.3.8.tgz",
+			"integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ="
+		},
+		"dom-helpers": {
+			"version": "3.4.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/dom-helpers/download/dom-helpers-3.4.0.tgz",
+			"integrity": "sha1-6bNpcA+Vn2Ls3lprq95LzNkWmvg=",
+			"requires": {
+				"@babel/runtime": "^7.1.2"
+			}
+		},
+		"esutils": {
+			"version": "2.0.2",
+			"resolved": "http://registry.npm.alibaba-inc.com/esutils/download/esutils-2.0.2.tgz",
+			"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
+		},
+		"has-flag": {
+			"version": "3.0.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/has-flag/download/has-flag-3.0.0.tgz",
+			"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+		},
+		"hoist-non-react-statics": {
+			"version": "2.5.5",
+			"resolved": "http://registry.npm.alibaba-inc.com/hoist-non-react-statics/download/hoist-non-react-statics-2.5.5.tgz",
+			"integrity": "sha1-xZA89AnA39kI84jmGdhrnBF0y0c="
+		},
+		"invariant": {
+			"version": "2.2.4",
+			"resolved": "http://registry.npm.alibaba-inc.com/invariant/download/invariant-2.2.4.tgz",
+			"integrity": "sha1-YQ88ksk1nOHbYW5TgAjSP/NRWOY=",
+			"requires": {
+				"loose-envify": "^1.0.0"
+			}
+		},
+		"js-tokens": {
+			"version": "4.0.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/js-tokens/download/js-tokens-4.0.0.tgz",
+			"integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk="
+		},
+		"lodash": {
+			"version": "4.17.11",
+			"resolved": "http://registry.npm.alibaba-inc.com/lodash/download/lodash-4.17.11.tgz",
+			"integrity": "sha1-s56mIp72B+zYniyN8SU2iRysm40="
+		},
+		"lodash.isequal": {
+			"version": "4.5.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/lodash.isequal/download/lodash.isequal-4.5.0.tgz",
+			"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+		},
+		"lodash.merge": {
+			"version": "4.6.1",
+			"resolved": "http://registry.npm.alibaba-inc.com/lodash.merge/download/lodash.merge-4.6.1.tgz",
+			"integrity": "sha1-rcJdnLmbk5HFliTzefu6YNcRHVQ="
+		},
+		"lodash.pick": {
+			"version": "4.4.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/lodash.pick/download/lodash.pick-4.4.0.tgz",
+			"integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM="
+		},
+		"lodash.pickby": {
+			"version": "4.6.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/lodash.pickby/download/lodash.pickby-4.6.0.tgz",
+			"integrity": "sha1-feoh2MGNdwOifHBMFdO4SmfjOv8="
+		},
+		"lodash.remove": {
+			"version": "4.7.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/lodash.remove/download/lodash.remove-4.7.0.tgz",
+			"integrity": "sha1-8x0x58OaBpDVB07A02JxYjNO5iY="
+		},
+		"lodash.uniqby": {
+			"version": "4.7.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/lodash.uniqby/download/lodash.uniqby-4.7.0.tgz",
+			"integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI="
+		},
+		"loose-envify": {
+			"version": "1.4.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/loose-envify/download/loose-envify-1.4.0.tgz",
+			"integrity": "sha1-ce5R+nvkyuwaY4OffmgtgTLTDK8=",
+			"requires": {
+				"js-tokens": "^3.0.0 || ^4.0.0"
+			}
+		},
+		"lower-case": {
+			"version": "1.1.4",
+			"resolved": "http://registry.npm.alibaba-inc.com/lower-case/download/lower-case-1.1.4.tgz",
+			"integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw="
+		},
+		"memoize-one": {
+			"version": "4.0.3",
+			"resolved": "http://registry.npm.alibaba-inc.com/memoize-one/download/memoize-one-4.0.3.tgz",
+			"integrity": "sha1-zf3ZQoU/GhtMccUza4xJ2gvwJzw="
+		},
+		"moment": {
+			"version": "2.24.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/moment/download/moment-2.24.0.tgz",
+			"integrity": "sha1-DQVdU/UFKqZTyfbraLtdEr9cK1s="
+		},
+		"monaco-editor": {
+			"version": "0.15.6",
+			"resolved": "http://registry.npm.alibaba-inc.com/monaco-editor/download/monaco-editor-0.15.6.tgz",
+			"integrity": "sha1-1js7BvhvgDRk8AOyUmJ8PrSglIM="
+		},
+		"no-case": {
+			"version": "2.3.2",
+			"resolved": "http://registry.npm.alibaba-inc.com/no-case/download/no-case-2.3.2.tgz",
+			"integrity": "sha1-YLgTOWvjmz8SiKTB7V0efSi0ZKw=",
+			"requires": {
+				"lower-case": "^1.1.1"
+			}
+		},
+		"object-assign": {
+			"version": "4.1.1",
+			"resolved": "http://registry.npm.alibaba-inc.com/object-assign/download/object-assign-4.1.1.tgz",
+			"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+		},
+		"pascal-case": {
+			"version": "2.0.1",
+			"resolved": "http://registry.npm.alibaba-inc.com/pascal-case/download/pascal-case-2.0.1.tgz",
+			"integrity": "sha1-LVeNNFX2YNpl7KGO+VtODekSdh4=",
+			"requires": {
+				"camel-case": "^3.0.0",
+				"upper-case-first": "^1.1.0"
+			}
+		},
+		"postcss-value-parser": {
+			"version": "3.3.1",
+			"resolved": "http://registry.npm.alibaba-inc.com/postcss-value-parser/download/postcss-value-parser-3.3.1.tgz",
+			"integrity": "sha1-n/giVH4okyE88cMO+lGsX9G6goE="
+		},
+		"prop-types": {
+			"version": "15.7.2",
+			"resolved": "http://registry.npm.alibaba-inc.com/prop-types/download/prop-types-15.7.2.tgz",
+			"integrity": "sha1-UsQedbjIfnK52TYOAga5ncv/psU=",
+			"requires": {
+				"loose-envify": "^1.4.0",
+				"object-assign": "^4.1.1",
+				"react-is": "^16.8.1"
+			}
+		},
+		"react-eva": {
+			"version": "1.0.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/react-eva/download/react-eva-1.0.0.tgz",
+			"integrity": "sha1-IRQP0wTADspGoQcJhUB3DNVay7c=",
+			"requires": {
+				"rxjs": "^6.3.3"
+			}
+		},
+		"react-is": {
+			"version": "16.8.3",
+			"resolved": "http://registry.npm.alibaba-inc.com/react-is/download/react-is-16.8.3.tgz",
+			"integrity": "sha1-StiwKcKnGPwM/HRsjU4bciHlOH0="
+		},
+		"react-lifecycles-compat": {
+			"version": "3.0.4",
+			"resolved": "http://registry.npm.alibaba-inc.com/react-lifecycles-compat/download/react-lifecycles-compat-3.0.4.tgz",
+			"integrity": "sha1-TxonOv38jzSIqMUWv9p4+HI1I2I="
+		},
+		"react-powerplug": {
+			"version": "1.0.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/react-powerplug/download/react-powerplug-1.0.0.tgz",
+			"integrity": "sha1-+cEKdh7OEVZhuP0QkgxOVz6pWQk=",
+			"requires": {
+				"@babel/runtime": "^7.0.0"
+			}
+		},
+		"react-redux": {
+			"version": "5.1.1",
+			"resolved": "http://registry.npm.alibaba-inc.com/react-redux/download/react-redux-5.1.1.tgz",
+			"integrity": "sha1-iONoaCx/qA404FXNesVvWTaw9S8=",
+			"requires": {
+				"@babel/runtime": "^7.1.2",
+				"hoist-non-react-statics": "^3.1.0",
+				"invariant": "^2.2.4",
+				"loose-envify": "^1.1.0",
+				"prop-types": "^15.6.1",
+				"react-is": "^16.6.0",
+				"react-lifecycles-compat": "^3.0.0"
+			},
+			"dependencies": {
+				"hoist-non-react-statics": {
+					"version": "3.3.0",
+					"resolved": "http://registry.npm.alibaba-inc.com/hoist-non-react-statics/download/hoist-non-react-statics-3.3.0.tgz",
+					"integrity": "sha1-sJF48BIhhPuVrPUl2q7LTY9FlYs=",
+					"requires": {
+						"react-is": "^16.7.0"
+					}
+				}
+			}
+		},
+		"react-transition-group": {
+			"version": "2.5.3",
+			"resolved": "http://registry.npm.alibaba-inc.com/react-transition-group/download/react-transition-group-2.5.3.tgz",
+			"integrity": "sha1-Jt42PKsZ5ciK5duuEFxwbPlTu5I=",
+			"requires": {
+				"dom-helpers": "^3.3.1",
+				"loose-envify": "^1.4.0",
+				"prop-types": "^15.6.2",
+				"react-lifecycles-compat": "^3.0.4"
+			}
+		},
+		"redux": {
+			"version": "4.0.1",
+			"resolved": "http://registry.npm.alibaba-inc.com/redux/download/redux-4.0.1.tgz",
+			"integrity": "sha1-Q2yubMQPvkcnaJ18j65EgI8b/vU=",
+			"requires": {
+				"loose-envify": "^1.4.0",
+				"symbol-observable": "^1.2.0"
+			}
+		},
+		"redux-logger": {
+			"version": "3.0.6",
+			"resolved": "http://registry.npm.alibaba-inc.com/redux-logger/download/redux-logger-3.0.6.tgz",
+			"integrity": "sha1-91VZZvMJjzyIYExEnPC69XeCdL8=",
+			"requires": {
+				"deep-diff": "^0.3.5"
+			}
+		},
+		"redux-thunk": {
+			"version": "2.3.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/redux-thunk/download/redux-thunk-2.3.0.tgz",
+			"integrity": "sha1-UcLBmhhe1Rh6qpotCLZm0NZGdiI="
+		},
+		"regenerator-runtime": {
+			"version": "0.11.1",
+			"resolved": "http://registry.npm.alibaba-inc.com/regenerator-runtime/download/regenerator-runtime-0.11.1.tgz",
+			"integrity": "sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk="
+		},
+		"rxjs": {
+			"version": "6.4.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/rxjs/download/rxjs-6.4.0.tgz",
+			"integrity": "sha1-87sP572n+2nerAwW8XtQsLh5BQQ=",
+			"requires": {
+				"tslib": "^1.9.0"
+			}
+		},
+		"shallow-element-equals": {
+			"version": "1.0.1",
+			"resolved": "http://registry.npm.alibaba-inc.com/shallow-element-equals/download/shallow-element-equals-1.0.1.tgz",
+			"integrity": "sha1-UHObfZStdWehNBc9P0QiOH7VfOY=",
+			"requires": {
+				"style-equal": "^1.0.0"
+			}
+		},
+		"style-equal": {
+			"version": "1.0.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/style-equal/download/style-equal-1.0.0.tgz",
+			"integrity": "sha1-mKHFkiImv+E8GW5z8ZQOkbjmZZU="
+		},
+		"styled-components": {
+			"version": "4.1.3",
+			"resolved": "http://registry.npm.alibaba-inc.com/styled-components/download/styled-components-4.1.3.tgz",
+			"integrity": "sha1-RHJEcgjmGLV+hN6q62rNNKXg/ps=",
+			"requires": {
+				"@babel/helper-module-imports": "^7.0.0",
+				"@emotion/is-prop-valid": "^0.7.3",
+				"@emotion/unitless": "^0.7.0",
+				"babel-plugin-styled-components": ">= 1",
+				"css-to-react-native": "^2.2.2",
+				"memoize-one": "^4.0.0",
+				"prop-types": "^15.5.4",
+				"react-is": "^16.6.0",
+				"stylis": "^3.5.0",
+				"stylis-rule-sheet": "^0.0.10",
+				"supports-color": "^5.5.0"
+			}
+		},
+		"stylis": {
+			"version": "3.5.4",
+			"resolved": "http://registry.npm.alibaba-inc.com/stylis/download/stylis-3.5.4.tgz",
+			"integrity": "sha1-9mXyX14pnPPWRlSrlJpXx2i3P74="
+		},
+		"stylis-rule-sheet": {
+			"version": "0.0.10",
+			"resolved": "http://registry.npm.alibaba-inc.com/stylis-rule-sheet/download/stylis-rule-sheet-0.0.10.tgz",
+			"integrity": "sha1-ROZKKwdmQ/S1Ll/3HvwE2MPEpDA="
+		},
+		"supports-color": {
+			"version": "5.5.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/supports-color/download/supports-color-5.5.0.tgz",
+			"integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=",
+			"requires": {
+				"has-flag": "^3.0.0"
+			}
+		},
+		"symbol-observable": {
+			"version": "1.2.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/symbol-observable/download/symbol-observable-1.2.0.tgz",
+			"integrity": "sha1-wiaIrtTqs83C3+rLtWFmBWCgCAQ="
+		},
+		"to-fast-properties": {
+			"version": "2.0.0",
+			"resolved": "http://registry.npm.alibaba-inc.com/to-fast-properties/download/to-fast-properties-2.0.0.tgz",
+			"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
+		},
+		"tslib": {
+			"version": "1.9.3",
+			"resolved": "http://registry.npm.alibaba-inc.com/tslib/download/tslib-1.9.3.tgz",
+			"integrity": "sha1-1+TdeSRdhUKMTX5IIqeZF5VMooY="
+		},
+		"upper-case": {
+			"version": "1.1.3",
+			"resolved": "http://registry.npm.alibaba-inc.com/upper-case/download/upper-case-1.1.3.tgz",
+			"integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg="
+		},
+		"upper-case-first": {
+			"version": "1.1.2",
+			"resolved": "http://registry.npm.alibaba-inc.com/upper-case-first/download/upper-case-first-1.1.2.tgz",
+			"integrity": "sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU=",
+			"requires": {
+				"upper-case": "^1.1.1"
+			}
+		},
+		"uuid": {
+			"version": "3.3.2",
+			"resolved": "http://registry.npm.alibaba-inc.com/uuid/download/uuid-3.3.2.tgz",
+			"integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE="
+		}
+	}
+}

+ 40 - 10
packages/builder/package.json

@@ -1,12 +1,15 @@
 {
   "name": "@uform/builder",
-  "version": "0.1.0-alpha.11",
+  "version": "0.1.0-alpha.12",
   "license": "MIT",
   "main": "lib/index.js",
   "repository": {
     "type": "git",
     "url": "git+https://github.com/alibaba/uform.git"
   },
+  "scripts": {
+    "start": "node scripts/start.js"
+  },
   "bugs": {
     "url": "https://github.com/alibaba/uform/issues"
   },
@@ -15,10 +18,12 @@
     "npm": ">=3.0.0"
   },
   "dependencies": {
-    "@alifd/next": "^1.13.1",
-    "@uform/react": "^0.1.0-alpha.11",
-    "@uform/utils": "^0.1.0-alpha.10",
-    "@uform/validator": "^0.1.0-alpha.10",
+    "moment": "^2.24.0",
+    "@uform/react": "^0.1.0-alpha.3",
+    "@uform/utils": "^0.1.0-alpha.3",
+    "@uform/validator": "^0.1.0-alpha.3",
+    "pascal-case": "^2.0.1",
+    "react-eva": "^1.0.0",
     "classnames": "^2.2.5",
     "lodash.isequal": "^4.5.0",
     "lodash.merge": "^4.6.1",
@@ -26,21 +31,46 @@
     "lodash.pickby": "^4.6.0",
     "lodash.remove": "^4.7.0",
     "lodash.uniqby": "^4.7.0",
-    "moment": "^2.24.0",
     "monaco-editor": "^0.15.6",
-    "pascal-case": "^2.0.1",
     "prop-types": "^15.6.1",
-    "react-eva": "^1.0.0",
     "react-powerplug": "^1.0.0",
     "react-redux": "^5.0.7",
     "redux": "^4.0.0",
     "redux-logger": "^3.0.6",
     "redux-thunk": "^2.2.0",
     "styled-components": "^4.1.2",
-    "uuid": "^3.2.1"
+    "uuid": "^3.2.1",
+    "@alifd/next": "^1.13.1"
   },
   "publishConfig": {
     "access": "public"
   },
-  "gitHead": "f513fc2dcca781b3f7aa588c4419bce20cba2d8b"
+  "gitHead": "f513fc2dcca781b3f7aa588c4419bce20cba2d8b",
+  "devDependencies": {
+    "@alifd/next": "^1.13.1",
+    "@babel/plugin-proposal-class-properties": "^7.3.4",
+    "@babel/plugin-proposal-decorators": "^7.3.0",
+    "@babel/plugin-syntax-dynamic-import": "^7.2.0",
+    "@babel/plugin-transform-runtime": "^7.3.4",
+    "@babel/preset-env": "^7.3.4",
+    "@uform/next": "^0.1.0-alpha.9",
+    "autoprefixer": "7.1.6",
+    "case-sensitive-paths-webpack-plugin": "2.1.1",
+    "css-loader": "0.28.7",
+    "eslint-loader": "1.9.0",
+    "file-loader": "1.1.5",
+    "html-webpack-plugin": "2.29.0",
+    "less": "^3.0.4",
+    "less-loader": "^4.1.0",
+    "postcss-flexbugs-fixes": "3.2.0",
+    "postcss-loader": "2.0.8",
+    "raf": "3.4.0",
+    "react-dev-utils": "^5.0.1",
+    "react-test-renderer": "^16.8.3",
+    "redux-mock-store": "^1.5.3",
+    "style-loader": "0.19.0",
+    "url-loader": "0.6.2",
+    "webpack": "3.8.1",
+    "webpack-dev-server": "2.9.4"
+  }
 }

BIN
packages/builder/public/favicon.ico


+ 40 - 0
packages/builder/public/index.html

@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <meta name="theme-color" content="#000000">
+    <!--
+      manifest.json provides metadata used when your web app is added to the
+      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
+    -->
+    <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
+    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
+    <!--
+      Notice the use of %PUBLIC_URL% in the tags above.
+      It will be replaced with the URL of the `public` folder during the build.
+      Only files inside the `public` folder can be referenced from the HTML.
+
+      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
+      work correctly both with client-side routing and a non-root public URL.
+      Learn how to configure a non-root public URL by running `npm run build`.
+    -->
+    <title>可视化表单搭建</title>
+  </head>
+  <body>
+    <noscript>
+      You need to enable JavaScript to run this app.
+    </noscript>
+    <div id="root"></div>
+    <!--
+      This HTML file is a template.
+      If you open it directly in the browser, you will see an empty page.
+
+      You can add webfonts, meta tags, or analytics to this file.
+      The build step will place the bundled scripts into the <body> tag.
+
+      To begin the development, run `npm start` or `yarn start`.
+      To create a production bundle, use `npm run build` or `yarn build`.
+    -->
+  </body>
+</html>

+ 15 - 0
packages/builder/public/manifest.json

@@ -0,0 +1,15 @@
+{
+  "short_name": "React App",
+  "name": "Create React App Sample",
+  "icons": [
+    {
+      "src": "favicon.ico",
+      "sizes": "64x64 32x32 24x24 16x16",
+      "type": "image/x-icon"
+    }
+  ],
+  "start_url": "./index.html",
+  "display": "standalone",
+  "theme_color": "#000000",
+  "background_color": "#ffffff"
+}

+ 90 - 0
packages/builder/scripts/start.js

@@ -0,0 +1,90 @@
+// Do this as the first thing so that any code reading it knows the right env.
+process.env.BABEL_ENV = 'development'
+process.env.NODE_ENV = 'development'
+
+// Makes the script crash on unhandled rejections instead of silently
+// ignoring them. In the future, promise rejections that are not handled will
+// terminate the Node.js process with a non-zero exit code.
+process.on('unhandledRejection', err => {
+  throw err
+})
+
+// Ensure environment variables are read.
+require('../config/env')
+
+const fs = require('fs')
+const chalk = require('chalk')
+const webpack = require('webpack')
+const WebpackDevServer = require('webpack-dev-server')
+const clearConsole = require('react-dev-utils/clearConsole')
+const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles')
+const {
+  choosePort,
+  createCompiler,
+  prepareProxy,
+  prepareUrls
+} = require('react-dev-utils/WebpackDevServerUtils')
+const openBrowser = require('react-dev-utils/openBrowser')
+const paths = require('../config/paths')
+const config = require('../config/webpack.config.dev')
+const createDevServerConfig = require('../config/webpackDevServer.config')
+
+const useYarn = fs.existsSync(paths.yarnLockFile)
+const isInteractive = process.stdout.isTTY
+
+// Warn and crash if required files are missing
+if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
+  process.exit(1)
+}
+
+// Tools like Cloud9 rely on this.
+const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000
+const HOST = process.env.HOST || '0.0.0.0'
+
+// We attempt to use the default port but if it is busy, we offer the user to
+// run on a different port. `detect()` Promise resolves to the next free port.
+choosePort(HOST, DEFAULT_PORT)
+  .then(port => {
+    if (port == null) {
+      // We have not found a port.
+      return
+    }
+    const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'
+    const appName = require(paths.appPackageJson).name
+    const urls = prepareUrls(protocol, HOST, port)
+    // Create a webpack compiler that is configured with custom messages.
+    const compiler = createCompiler(webpack, config, appName, urls, useYarn)
+    // Load proxy config
+    const proxySetting = require(paths.appPackageJson).proxy
+    const proxyConfig = prepareProxy(proxySetting, paths.appPublic)
+    // Serve webpack assets generated by the compiler over a web sever.
+    const serverConfig = createDevServerConfig(
+      proxyConfig,
+      urls.lanUrlForConfig
+    )
+    const devServer = new WebpackDevServer(compiler, serverConfig)
+    // Launch WebpackDevServer.
+    devServer.listen(port, HOST, err => {
+      if (err) {
+        return console.log(err)
+      }
+      if (isInteractive) {
+        clearConsole()
+      }
+      console.log(chalk.cyan('Starting the development server...\n'))
+      openBrowser(urls.localUrlForBrowser)
+    })
+
+    ;['SIGINT', 'SIGTERM'].forEach(sig => {
+      process.on(sig, () => {
+        devServer.close()
+        process.exit()
+      })
+    })
+  })
+  .catch(err => {
+    if (err && err.message) {
+      console.log(err.message)
+    }
+    process.exit(1)
+  })

+ 4 - 9
packages/builder/src/App.js

@@ -14,8 +14,7 @@ import {
 import { Divider } from './utils/util'
 
 import isEqual from 'lodash.isequal'
-import styled from 'styled-components'
-import appStyle from './style'
+import AppStyle from './style'
 
 // components
 import FieldList from './components/fields/index'
@@ -145,7 +144,7 @@ class App extends Component {
     return this.state.systemError ? (
       <p>系统发生异常</p>
     ) : (
-      <div className={cls('schemaform-app', this.props.className)}>
+      <AppStyle className={cls('schemaform-app', this.props.className)}>
         <div className='schemaform-header'>
           <a
             href='javascript:;'
@@ -177,7 +176,7 @@ class App extends Component {
           </div>
         </div>
         {this.getEditorTpl()}
-      </div>
+      </AppStyle>
     )
   }
 }
@@ -249,13 +248,9 @@ const mapDispatchToProps = dispatch => ({
     dispatch(editComponent(id, propsData, containerId))
 })
 
-const StyledApp = styled(App)`
-  ${appStyle}
-`
-
 class StyledAppComp extends React.Component {
   render() {
-    return <StyledApp {...this.props} />
+    return <App {...this.props} />
   }
 }
 

+ 26 - 0
packages/builder/src/__tests__/__snapshots__/comp.js.snap

@@ -0,0 +1,26 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`shoud correct render comp 1`] = `
+<div
+  className="sc-bdVaJa fjjTqK"
+/>
+`;
+
+exports[`shoud correct render comp 2`] = `
+<div
+  className="aa sc-bwzfXH fsBqau"
+>
+  hello
+</div>
+`;
+
+exports[`shoud correct render comp 3`] = `
+<i
+  className="aaa sc-htpNat bpNkmf"
+  style={
+    Object {
+      "backgroundImage": "url(https://www.tmall.com)",
+    }
+  }
+/>
+`;

+ 26 - 0
packages/builder/src/__tests__/__snapshots__/comp.spec.js.snap

@@ -0,0 +1,26 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`shoud correct render comp 1`] = `
+<div
+  className="sc-bdVaJa fjjTqK"
+/>
+`;
+
+exports[`shoud correct render comp 2`] = `
+<div
+  className="aa sc-bwzfXH fsBqau"
+>
+  hello
+</div>
+`;
+
+exports[`shoud correct render comp 3`] = `
+<i
+  className="aaa sc-htpNat bpNkmf"
+  style={
+    Object {
+      "backgroundImage": "url(https://www.tmall.com)",
+    }
+  }
+/>
+`;

+ 146 - 0
packages/builder/src/__tests__/actions/index.spec.js

@@ -0,0 +1,146 @@
+import * as actions from '../../actions/index'
+
+describe('test actions', () => {
+  test('changeLayoutId action', () => {
+    expect(actions.changeLayoutId('111')).toEqual({
+      type: 'CHANGE_LAYOUTID',
+      data: {
+        id: '111'
+      }
+    })
+  })
+
+  test('addComponent action', () => {
+    expect(actions.addComponent({
+      type: 'string',
+      title: '111'
+    }, '222', '333', 'layout', '444')).toEqual({
+      type: 'ADD_COMPONENT',
+      data: {
+        id: '333',
+        component: {
+          type: 'string',
+          title: '111'
+        },
+        existId: '222',
+        addType: 'layout',
+        containerId: '444'
+      }
+    })
+  })
+
+  test('changeComponentOrder action', () => {
+    expect(actions.changeComponentOrder('111', '222', '333')).toEqual({
+      type: 'CHANGE_COMPONENT_ORDER',
+      data: {
+        id: '111',
+        targetId: '222',
+        containerId: '333'
+      }
+    })
+  })
+
+  test('editComponent action', () => {
+    expect(actions.editComponent('111', {
+      __id__: 222
+    }, '333')).toEqual({
+      type: 'EDIT_COMPONENT',
+      data: {
+        id: '111',
+        propsData: {
+          __id__: 222
+        },
+        containerId: '333'
+      }
+    })
+  })
+
+  test('deleteComponent action', () => {
+    expect(actions.deleteComponent('111')).toEqual({
+      type: 'DELETE_COMPONENT',
+      data: {
+        id: '111'
+      }
+    })
+  })
+
+  test('showComponentProps action', () => {
+    expect(actions.showComponentProps('111', {
+      type: 'string',
+      title: '222'
+    }, '333')).toEqual({
+      type: 'SHOW_COMPONENT_PROPS',
+      data: {
+        id: '111',
+        comp: {
+          type: 'string',
+          title: '222'
+        },
+        containerId: '333'
+      }
+    })
+  })
+
+  test('editComponentProps action', () => {
+    expect(actions.editComponentProps('111', {
+      __id__: '222'
+    })).toEqual({
+      type: 'EDIT_COMPONENT_PROPS',
+      data: {
+        id: '111',
+        propsData: {
+          __id__: '222'
+        }
+      }
+    })
+  })
+
+  test('changePreview action', () => {
+    expect(actions.changePreview(true)).toEqual({
+      type: 'CHANGE_PREVIEW',
+      data: {
+        preview: true
+      }
+    })
+  })
+
+  test('changeCodeMode action', () => {
+    expect(actions.changeCodeMode(true)).toEqual({
+      type: 'CHANGE_CODEMODE',
+      data: {
+        codemode: true
+      }
+    })
+  })
+
+  test('changeComponent action', () => {
+    expect(actions.changeComponent('111')).toEqual({
+      type: 'CHANGE_COMPONENT',
+      data: {
+        componentId: '111'
+      }
+    })
+  })
+
+  test('changeGbConfig action', () => {
+    expect(actions.changeGbConfig({
+      aaa: 1
+    })).toEqual({
+      type: 'CHANGE_GB_CONFIG',
+      data: {
+        aaa: 1
+      }
+    })
+  })
+
+  test('initSchema action', () => {
+    expect(actions.initSchema({
+      aaa: 1
+    })).toEqual({
+      type: 'INIT_SCHEMA',
+      data: {
+        aaa: 1
+      }
+    })
+  })
+})

+ 8 - 0
packages/builder/src/__tests__/baseForm.spec.js

@@ -0,0 +1,8 @@
+import * as Form from '../utils/baseForm'
+
+test('test baseForm', () => {
+  expect(Form).toHaveProperty('SchemaForm')
+  expect(Form).toHaveProperty('Field')
+  expect(Form).toHaveProperty('registerFormField')
+  expect(Form).toHaveProperty('registerFormFields')
+})

+ 22 - 0
packages/builder/src/__tests__/comp.spec.js

@@ -0,0 +1,22 @@
+
+import React from 'react'
+import renderer from 'react-test-renderer'
+import * as Comp from '../utils/comp'
+
+test('shoud correct render comp', () => {
+  const comp1 = renderer.create(
+    <Comp.Divider />
+  )
+  const comp2 = renderer.create(
+    <Comp.Header className='aa' theme={{ whiteColor: '#fff' }}>hello</Comp.Header>
+  )
+  const comp3 = renderer.create(
+    <Comp.CustomIcon className='aaa' iconUrl='https://www.tmall.com' width='15' height='15' />
+  )
+  let tree1 = comp1.toJSON()
+  let tree2 = comp2.toJSON()
+  let tree3 = comp3.toJSON()
+  expect(tree1).toMatchSnapshot()
+  expect(tree2).toMatchSnapshot()
+  expect(tree3).toMatchSnapshot()
+})

+ 15 - 0
packages/builder/src/__tests__/index.spec.js

@@ -0,0 +1,15 @@
+import React from 'react'
+import ShallowRenderer from 'react-test-renderer/shallow'
+import Index from '../index'
+
+const renderer = new ShallowRenderer()
+
+test('shoud correct render APP', () => {
+  const _props = {
+    renderEngine: class extends React.Component {}
+  }
+  renderer.render(<Index {..._props} />)
+  const result = renderer.getRenderOutput()
+  const matchProps = result.props.children.props.children.props
+  expect(matchProps).toHaveProperty('renderEngine')
+})

+ 48 - 2
packages/builder/src/__tests__/lang.spec.js

@@ -1,4 +1,21 @@
-import { normalizeSchema } from '../utils/lang'
+import * as lang from '../utils/lang'
+
+test('lang funtions', () => {
+  expect(lang.isType('Array')([])).not.toBeFalsy()
+  expect(lang.isType('Array')('')).toBeFalsy()
+  expect(lang.isFn(function() {})).not.toBeFalsy()
+  expect(lang.isFn('')).toBeFalsy()
+  expect(lang.isArr([])).not.toBeFalsy()
+  expect(lang.isArr('111')).toBeFalsy()
+  expect(lang.isObj({})).not.toBeFalsy()
+  expect(lang.isObj('111')).toBeFalsy()
+  expect(lang.isStr('')).not.toBeFalsy()
+  expect(lang.isStr(123)).toBeFalsy()
+  expect(lang.isNum(111)).not.toBeFalsy()
+  expect(lang.isNum('hello')).toBeFalsy()
+  expect(lang.isIter([1, 2])).not.toBeFalsy()
+  expect(lang.isIter(1)).toBeFalsy()
+})
 
 test('should correct normalize schema', () => {
   var schema = {
@@ -28,7 +45,21 @@ test('should correct normalize schema', () => {
       }
     }
   }
-  var value = normalizeSchema(schema)
+  var schema1 = {
+    'type': 'object',
+    'properties': {
+      '[startDate,endDate]': {
+        'type': 'daterange',
+        'default': [
+          { type: 'specify', value: '2019-01-01', flag: 'date' },
+          { type: 'specify', value: '2019-01-02', flag: 'date' }
+        ],
+        'z-index': 0
+      }
+    }
+  }
+  var value = lang.normalizeSchema(schema)
+  var value1 = lang.normalizeSchema(schema1)
   var result = {
     'type': 'object',
     'properties': {
@@ -56,5 +87,20 @@ test('should correct normalize schema', () => {
       }
     }
   }
+  var result1 = {
+    'type': 'object',
+    'properties': {
+      '[startDate,endDate]': {
+        'type': 'daterange',
+        'default': [
+          '2019-01-01',
+          '2019-01-02'
+        ],
+        'z-index': 0
+      }
+    }
+  }
   expect(value).toEqual(result)
+  expect(value1).toEqual(result1)
+  expect(lang.normalizeSchema()).toEqual(null)
 })

+ 702 - 0
packages/builder/src/__tests__/reducers/index.spec.js

@@ -0,0 +1,702 @@
+import codemodeReducer from '../../reducers/codemode'
+import componentIdReducer from '../../reducers/componentId'
+import componentPropsReducer from '../../reducers/componentProps'
+import gbConfigReducer from '../../reducers/gbConfig'
+import initSchemaDataReducer from '../../reducers/initSchemaData'
+import layoutIdReducer from '../../reducers/layoutId'
+import previewReducer from '../../reducers/preview'
+
+describe('reducers', () => {
+  // codemode reducer
+  test('codemode reducers return initial state', () => {
+    expect(codemodeReducer(undefined, {})).toEqual(false)
+  })
+  test('codemode reducers return custom state', () => {
+    const beforeState = false
+    const action = {
+      type: 'CHANGE_CODEMODE',
+      data: {
+        codemode: true
+      }
+    }
+    expect(codemodeReducer(beforeState, action)).toEqual(true)
+  })
+
+  // componentId reducer
+  test('componentId reducers return initial state', () => {
+    expect(componentIdReducer(undefined, {})).toEqual('')
+  })
+  test('componentId reducers return custom state', () => {
+    const beforeState = ''
+    const action = {
+      type: 'CHANGE_COMPONENT',
+      data: {
+        componentId: '66666'
+      }
+    }
+    expect(componentIdReducer(beforeState, action)).toEqual('66666')
+  })
+
+  // componentProps reducer
+  test('componentProps reducers return initial state', () => {
+    expect(componentPropsReducer(undefined, {})).toEqual({})
+  })
+  test('componentProps reducers return custom state when SHOW_COMPONENT_PROPS', () => {
+    const beforeState = {}
+    const action = {
+      type: 'SHOW_COMPONENT_PROPS',
+      data: {
+        id: '11111',
+        comp: {
+          key: 'input',
+          icon: 'info',
+          iconUrl: '//gw.alicdn.com/tfs/TB11eW6DmzqK1RjSZFpXXakSXXa-116-56.png',
+          width: '58',
+          height: '28',
+          type: 'string',
+          title: '单行文本框',
+          placeholder: '请输入'
+        }
+      }
+    }
+
+    const afterState = {
+      '11111': [
+        {
+          name: '__id__',
+          title: '唯一标识',
+          type: 'string',
+          description: '发起请求时带过去的参数字段',
+          required: true,
+          value: '11111'
+        },
+        { name: 'description', title: '提示文案', type: 'string' },
+        {
+          name: 'title',
+          title: '标题',
+          type: 'string',
+          placeholder: '请输入字段名称,不超过50个字符',
+          value: '单行文本框'
+        },
+        {
+          name: 'default',
+          title: '默认值',
+          type: 'object',
+          'x-component': 'defaultValueCascader'
+        },
+        { name: 'required', title: '是否必填', type: 'boolean' },
+        { name: 'x-props.readOnly', title: '是否只读', type: 'boolean' },
+        { name: 'x-props.disabled', title: '是否禁用', type: 'boolean' },
+        { name: 'x-props.htmltype', title: '是否隐藏', type: 'boolean' },
+        { name: 'x-props.placeholder', title: '占位符', type: 'string' }
+      ]
+    }
+    expect(componentPropsReducer(beforeState, action)).toEqual(afterState)
+  })
+  test('componentProps reducers return custom state when DELETE_COMPONENT', () => {
+    const beforeState = {
+      '11111': [
+        {
+          name: '__id__',
+          title: '唯一标识',
+          type: 'string',
+          description: '发起请求时带过去的参数字段',
+          required: true,
+          value: '11111'
+        },
+        { name: 'description', title: '提示文案', type: 'string' },
+        {
+          name: 'title',
+          title: '标题',
+          type: 'string',
+          placeholder: '请输入字段名称,不超过50个字符',
+          value: '单行文本框'
+        },
+        {
+          name: 'default',
+          title: '默认值',
+          type: 'object',
+          'x-component': 'defaultValueCascader'
+        },
+        { name: 'required', title: '是否必填', type: 'boolean' },
+        { name: 'x-props.readOnly', title: '是否只读', type: 'boolean' },
+        { name: 'x-props.disabled', title: '是否禁用', type: 'boolean' },
+        { name: 'x-props.htmltype', title: '是否隐藏', type: 'boolean' },
+        { name: 'x-props.placeholder', title: '占位符', type: 'string' }
+      ]
+    }
+    const action = {
+      type: 'DELETE_COMPONENT',
+      data: {
+        id: '11111'
+      }
+    }
+
+    const afterState = {}
+    expect(componentPropsReducer(beforeState, action)).toEqual(afterState)
+  })
+  test('componentProps reducers return custom state when EDIT_COMPONENT_PROPS', () => {
+    const beforeState = {
+      '11111': [
+        {
+          name: '__id__',
+          title: '唯一标识',
+          type: 'string',
+          description: '发起请求时带过去的参数字段',
+          required: true,
+          value: '11111'
+        },
+        { name: 'description', title: '提示文案', type: 'string' },
+        {
+          name: 'title',
+          title: '标题',
+          type: 'string',
+          placeholder: '请输入字段名称,不超过50个字符',
+          value: '单行文本框'
+        },
+        {
+          name: 'default',
+          title: '默认值',
+          type: 'object',
+          'x-component': 'defaultValueCascader'
+        },
+        { name: 'required', title: '是否必填', type: 'boolean' },
+        { name: 'x-props.readOnly', title: '是否只读', type: 'boolean' },
+        { name: 'x-props.disabled', title: '是否禁用', type: 'boolean' },
+        { name: 'x-props.htmltype', title: '是否隐藏', type: 'boolean' },
+        { name: 'x-props.placeholder', title: '占位符', type: 'string' }
+      ]
+    }
+    const action = {
+      type: 'EDIT_COMPONENT_PROPS',
+      data: {
+        id: '11111',
+        propsData: {
+          __id__: 'hello'
+        }
+      }
+    }
+
+    const afterState = {
+      '11111': [
+        {
+          name: '__id__',
+          value: 'hello',
+          title: '唯一标识',
+          type: 'string',
+          description: '发起请求时带过去的参数字段',
+          required: true
+        },
+        { name: 'description', title: '提示文案', type: 'string' },
+        {
+          name: 'title',
+          title: '标题',
+          type: 'string',
+          placeholder: '请输入字段名称,不超过50个字符',
+          value: '单行文本框'
+        },
+        {
+          name: 'default',
+          title: '默认值',
+          type: 'object',
+          'x-component': 'defaultValueCascader'
+        },
+        { name: 'required', title: '是否必填', type: 'boolean' },
+        { name: 'x-props.readOnly', title: '是否只读', type: 'boolean' },
+        { name: 'x-props.disabled', title: '是否禁用', type: 'boolean' },
+        { name: 'x-props.htmltype', title: '是否隐藏', type: 'boolean' },
+        { name: 'x-props.placeholder', title: '占位符', type: 'string' }
+      ]
+    }
+    expect(componentPropsReducer(beforeState, action)).toEqual(afterState)
+  })
+
+  // gbConfig reducers
+  test('gbConfig reducers return initial state', () => {
+    expect(gbConfigReducer(undefined, {})).toEqual({
+      labelAlign: 'left',
+      labelTextAlign: 'left',
+      autoAddColon: true,
+      needFormButtonGroup: false,
+      inline: false,
+      size: 'medium'
+    })
+  })
+  test('gbConfig reducers return custom state', () => {
+    const beforeState = {
+      labelAlign: 'left',
+      labelTextAlign: 'left',
+      autoAddColon: true,
+      needFormButtonGroup: false,
+      inline: false,
+      size: 'small'
+    }
+
+    const action = {
+      type: 'CHANGE_GB_CONFIG',
+      data: {
+        size: 'large',
+        inline: true,
+        extra: 'extra'
+      }
+    }
+
+    const afterState = {
+      labelAlign: 'left',
+      labelTextAlign: 'left',
+      autoAddColon: true,
+      needFormButtonGroup: false,
+      inline: true,
+      size: 'large',
+      extra: 'extra'
+    }
+
+    expect(gbConfigReducer(beforeState, action)).toEqual(afterState)
+  })
+
+  // layoutId reducer
+  test('layoutId reducers return initial state', () => {
+    expect(layoutIdReducer(undefined, {})).toEqual('')
+  })
+  test('layoutId reducers return custom state', () => {
+    const beforeState = 'aaa'
+    const action = {
+      type: 'CHANGE_LAYOUTID',
+      data: {
+        id: 'bbb'
+      }
+    }
+    const afterState = 'bbb'
+    expect(layoutIdReducer(beforeState, action)).toEqual(afterState)
+  })
+
+  // preview reducer
+  test('preview reducers return initial state', () => {
+    expect(previewReducer(undefined, {})).toEqual(false)
+  })
+  test('preview reducers return custom state', () => {
+    const beforeState = false
+    const action = {
+      type: 'CHANGE_PREVIEW',
+      data: {
+        preview: true
+      }
+    }
+    const afterState = true
+    expect(previewReducer(beforeState, action)).toEqual(afterState)
+  })
+
+  // initSchemaData reducer
+  test('initSchemaData reducers return initial state', () => {
+    expect(initSchemaDataReducer(undefined, {})).toEqual({})
+  })
+  test('initSchemaData reducers return custom state when INIT_SCHEMA', () => {
+    const beforeState = {}
+    const action = {
+      type: 'INIT_SCHEMA',
+      data: {
+        type: 'object',
+        properties: {
+          a: {
+            type: 'string',
+            title: 'a'
+          },
+          b: {
+            type: 'string',
+            title: 'b'
+          }
+        }
+      }
+    }
+    const afterState = {
+      type: 'object',
+      properties: {
+        a: {
+          type: 'string',
+          title: 'a',
+          id: 'a',
+          'x-index': 0
+        },
+        b: {
+          type: 'string',
+          title: 'b',
+          id: 'b',
+          'x-index': 1
+        }
+      }
+    }
+    expect(initSchemaDataReducer(beforeState, action)).toEqual(afterState)
+  })
+  test('initSchemaData reducers return custom state when CHANGE_COMPONENT_ORDER', () => {
+    const beforeState = {
+      type: 'object',
+      properties: {
+        a: {
+          type: 'string',
+          title: 'a'
+        },
+        b: {
+          type: 'string',
+          title: 'b'
+        }
+      }
+    }
+    const action = {
+      type: 'CHANGE_COMPONENT_ORDER',
+      data: {
+        id: 'a',
+        targetId: 'b'
+      }
+    }
+    const afterState = {
+      type: 'object',
+      properties: {
+        a: {
+          type: 'string',
+          title: 'a',
+          id: 'a',
+          'x-index': 1
+        },
+        b: {
+          type: 'string',
+          title: 'b',
+          id: 'b',
+          'x-index': 0
+        }
+      }
+    }
+    expect(initSchemaDataReducer(beforeState, action)).toEqual(afterState)
+  })
+  test('initSchemaData reducers return custom state when ADD_COMPONENT', () => {
+    const beforeState = {
+      type: 'object',
+      properties: {}
+    }
+    const beforeStateWithData = {
+      type: 'object',
+      properties: {
+        '111': {
+          type: 'string',
+          title: '111'
+        },
+        '333': {
+          type: 'string',
+          title: '333'
+        }
+      }
+    }
+    const beforeStateWithLayout = {
+      type: 'object',
+      properties: {
+        '111': {
+          type: 'object',
+          'x-component': 'layout',
+          properties: {}
+        }
+      }
+    }
+    const action1 = {
+      type: 'ADD_COMPONENT',
+      data: {
+        id: '111',
+        component: {
+          key: 'input',
+          icon: 'info',
+          iconUrl: '//gw.alicdn.com/tfs/TB11eW6DmzqK1RjSZFpXXakSXXa-116-56.png',
+          width: '58',
+          height: '28',
+          type: 'string',
+          title: '单行文本框',
+          placeholder: '请输入'
+        }
+      }
+    }
+    const action2 = {
+      type: 'ADD_COMPONENT',
+      data: {
+        id: '111',
+        component: {
+          key: 'wrapper_layout',
+          icon: 'clock-circle-o',
+          type: 'object',
+          title: 'Layout布局',
+          __key__: 'layout',
+          __key__data__: {
+            'x-component': 'layout',
+            'x-props': {
+              labelCol: 8,
+              wrapperCol: 6
+            }
+          }
+        }
+      }
+    }
+    const action3 = {
+      type: 'ADD_COMPONENT',
+      data: {
+        id: '222',
+        addType: 'layout',
+        containerId: '111',
+        component: {
+          key: 'input',
+          icon: 'info',
+          iconUrl: '//gw.alicdn.com/tfs/TB11eW6DmzqK1RjSZFpXXakSXXa-116-56.png',
+          width: '58',
+          height: '28',
+          type: 'string',
+          title: '单行文本框',
+          placeholder: '请输入'
+        }
+      }
+    }
+    const action4 = {
+      type: 'ADD_COMPONENT',
+      data: {
+        id: '222',
+        existId: '333',
+        component: {
+          key: 'input',
+          icon: 'info',
+          iconUrl: '//gw.alicdn.com/tfs/TB11eW6DmzqK1RjSZFpXXakSXXa-116-56.png',
+          width: '58',
+          height: '28',
+          type: 'string',
+          title: '单行文本框',
+          placeholder: '请输入'
+        }
+      }
+    }
+
+    const afterState1 = {
+      type: 'object',
+      properties: {
+        '111': {
+          key: 'input',
+          icon: 'info',
+          iconUrl: '//gw.alicdn.com/tfs/TB11eW6DmzqK1RjSZFpXXakSXXa-116-56.png',
+          width: '58',
+          height: '28',
+          type: 'string',
+          title: '单行文本框',
+          placeholder: '请输入',
+          'x-index': 0,
+          id: '111',
+          __id__: '111'
+        }
+      }
+    }
+    const afterState2 = {
+      type: 'object',
+      properties: {
+        '111': {
+          type: 'object',
+          id: '111',
+          __id__: '111',
+          'x-component': 'layout',
+          properties: {},
+          'x-props': {
+            labelCol: 8,
+            wrapperCol: 6,
+            _extra: {
+              key: 'wrapper_layout',
+              icon: 'clock-circle-o',
+              type: 'object',
+              title: 'Layout布局',
+              __key__: 'layout',
+              __key__data__: {
+                'x-component': 'layout',
+                'x-props': {
+                  labelCol: 8,
+                  wrapperCol: 6
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+    const afterState3 = {
+      type: 'object',
+      properties: {
+        '111': {
+          type: 'object',
+          'x-component': 'layout',
+          properties: {
+            '222': {
+              key: 'input',
+              icon: 'info',
+              iconUrl:
+                '//gw.alicdn.com/tfs/TB11eW6DmzqK1RjSZFpXXakSXXa-116-56.png',
+              width: '58',
+              height: '28',
+              type: 'string',
+              title: '单行文本框',
+              placeholder: '请输入',
+              'x-index': 0,
+              id: '222',
+              __id__: '222'
+            }
+          }
+        }
+      }
+    }
+    const afterState4 = {
+      type: 'object',
+      properties: {
+        '111': {
+          type: 'string',
+          title: '111',
+          'x-index': 0,
+          id: '111',
+          __id__: '111'
+        },
+        '222': {
+          key: 'input',
+          icon: 'info',
+          iconUrl: '//gw.alicdn.com/tfs/TB11eW6DmzqK1RjSZFpXXakSXXa-116-56.png',
+          width: '58',
+          height: '28',
+          type: 'string',
+          title: '单行文本框',
+          placeholder: '请输入',
+          'x-index': 1,
+          id: '222',
+          __id__: '222'
+        },
+        '333': {
+          type: 'string',
+          title: '333',
+          'x-index': 2,
+          id: '333',
+          __id__: '333'
+        }
+      }
+    }
+    expect(initSchemaDataReducer(beforeState, action1)).toEqual(afterState1)
+    expect(initSchemaDataReducer(beforeState, action2)).toEqual(afterState2)
+    expect(initSchemaDataReducer(beforeStateWithLayout, action3)).toEqual(
+      afterState3
+    )
+    expect(initSchemaDataReducer(beforeStateWithData, action4)).toEqual(
+      afterState4
+    )
+  })
+  test('initSchemaData reducers return custom state when DELETE_COMPONENT', () => {
+    const beforeState = {
+      type: 'object',
+      properties: {
+        '111': {
+          type: 'string',
+          title: '111'
+        }
+      }
+    }
+    const action = {
+      type: 'DELETE_COMPONENT',
+      data: {
+        id: '111'
+      }
+    }
+    const afterState = {
+      type: 'object',
+      properties: {}
+    }
+    expect(initSchemaDataReducer(beforeState, action)).toEqual(afterState)
+  })
+  test('initSchemaData reducers return custom state when EDIT_COMPONENT', () => {
+    const beforeState = {
+      type: 'object',
+      properties: {
+        '111': {
+          type: 'string',
+          title: '111'
+        }
+      }
+    }
+    const beforeState1 = {
+      type: 'object',
+      properties: {
+        '111': {
+          type: 'object',
+          'x-component': 'layout',
+          properties: {
+            '222': {
+              type: 'string',
+              title: '222'
+            }
+          }
+        }
+      }
+    }
+    const action = {
+      type: 'EDIT_COMPONENT',
+      data: {
+        id: '111',
+        propsData: {
+          __id__: '222'
+        }
+      }
+    }
+    const action1 = {
+      type: 'EDIT_COMPONENT',
+      data: {
+        id: '222',
+        containerId: '111',
+        propsData: {
+          __id__: '333',
+          'x-props': {
+            enum: [
+              {
+                value: 1,
+                label: 1
+              }
+            ]
+          }
+        }
+      }
+    }
+    const afterState = {
+      type: 'object',
+      properties: {
+        '111': {
+          type: 'string',
+          title: '111',
+          __id__: '222'
+        }
+      }
+    }
+    const afterState1 = {
+      type: 'object',
+      properties: {
+        '111': {
+          type: 'object',
+          'x-component': 'layout',
+          properties: {
+            '222': {
+              __id__: '333',
+              type: 'string',
+              title: '222',
+              enum: [
+                {
+                  value: 1,
+                  label: 1
+                }
+              ],
+              'x-props': {
+                enum: [
+                  {
+                    value: 1,
+                    label: 1
+                  }
+                ]
+              }
+            }
+          }
+        }
+      }
+    }
+
+    expect(initSchemaDataReducer(beforeState, action)).toEqual(afterState)
+    expect(initSchemaDataReducer(beforeState1, action1)).toEqual(afterState1)
+  })
+})

+ 6 - 15
packages/builder/src/components/editor/index.js

@@ -1,10 +1,9 @@
 import React from 'react'
 import cls from 'classnames'
-import Dialog from '@alifd/next/lib/dialog'
-import styled from 'styled-components'
+
 import { connect } from 'react-redux'
 import { initSchema, changeGbConfig, changeCodeMode } from '../../actions/index'
-import editorStyle from './style'
+import EditorStyle from './style'
 import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'
 
 import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution'
@@ -48,7 +47,7 @@ class Component extends React.Component {
     const { className, codemode } = this.props
 
     return (
-      <div className={cls(className, codemode ? 'active' : '')}>
+      <EditorStyle className={cls(className, codemode ? 'active' : '')}>
         <div id='J_uformEditor' className='editor' />
         <a
           href='javascript:;'
@@ -67,28 +66,20 @@ class Component extends React.Component {
               _initSchema(schema)
               _changeGbConfig(gbConfig)
             } catch (e) {
-              Dialog.alert({
-                title: '提示',
-                content: '格式转换失败,请检查代码'
-              })
-              return false
+              throw new Error(`格式转换失败,请检查代码:  + ${e.message}`)
             }
           }}
         >
           保存源码
         </a>
-      </div>
+      </EditorStyle>
     )
   }
 }
 
-const StyledEditor = styled(Component)`
-  ${editorStyle}
-`
-
 class StyledEditorComp extends React.Component {
   render() {
-    return <StyledEditor {...this.props} />
+    return <Component {...this.props} />
   }
 }
 

+ 3 - 1
packages/builder/src/components/editor/style.js

@@ -1,4 +1,6 @@
-export default `
+import styled from 'styled-components'
+
+export default styled.div`
   position: absolute;
   min-width: 500px;
   top: 64px;

+ 4 - 9
packages/builder/src/components/fields/index.js

@@ -12,8 +12,7 @@ import {
   addComponentAndEdit
 } from '../../actions'
 import uniqBy from 'lodash.uniqby'
-import styled from 'styled-components'
-import { indexStyle } from './style'
+import { indexStyle as IndexStyle } from './style'
 
 class FieldList extends Component {
   static propTypes = {
@@ -104,13 +103,13 @@ class FieldList extends Component {
 
   render() {
     return (
-      <div className={cls('col-card col-fields', this.props.className)}>
+      <IndexStyle className={cls('col-card col-fields', this.props.className)}>
         <Header>
           <h2>组件</h2>
           <p>可将选项拖动到主面板进行编辑</p>
         </Header>
         {this.renderFieldList()}
-      </div>
+      </IndexStyle>
     )
   }
 }
@@ -126,13 +125,9 @@ const mapDispatchToProps = dispatch => ({
   addComponentAndEdit: component => dispatch(addComponentAndEdit(component))
 })
 
-const StyledFieldList = styled(FieldList)`
-  ${indexStyle}
-`
-
 class StyledFieldListComp extends React.Component {
   render() {
-    return <StyledFieldList {...this.props} />
+    return <FieldList {...this.props} />
   }
 }
 

+ 5 - 9
packages/builder/src/components/fields/layout.js

@@ -6,8 +6,8 @@ import supportLayoutList from '../../configs/supportLayoutList'
 import { Header } from '../../utils/util'
 import { connect } from 'react-redux'
 import { addComponentAndEdit } from '../../actions'
-import styled from 'styled-components'
-import { layoutStyle } from './style'
+
+import { layoutStyle as LayoutStyle } from './style'
 
 class Component extends React.Component {
   static propTypes = {
@@ -46,13 +46,13 @@ class Component extends React.Component {
 
   render() {
     return (
-      <div className={cls('col-card col-layout', this.props.className)}>
+      <LayoutStyle className={cls('col-card col-layout', this.props.className)}>
         <Header>
           <h2>布局</h2>
           <p>单击将布局添加入主面板</p>
         </Header>
         {this.renderList()}
-      </div>
+      </LayoutStyle>
     )
   }
 }
@@ -63,13 +63,9 @@ const mapDispatchToProps = dispatch => ({
   addComponentAndEdit: component => dispatch(addComponentAndEdit(component))
 })
 
-const StyledComponent = styled(Component)`
-  ${layoutStyle}
-`
-
 class StyledLayoutListComp extends React.Component {
   render() {
-    return <StyledComponent {...this.props} />
+    return <Component {...this.props} />
   }
 }
 

+ 4 - 2
packages/builder/src/components/fields/style.js

@@ -1,4 +1,6 @@
-export const indexStyle = `
+import styled from 'styled-components'
+
+export const indexStyle = styled.div`
   .field-list {
     font-size: 0;
     li {
@@ -42,7 +44,7 @@ export const indexStyle = `
   }
 `
 
-export const layoutStyle = `
+export const layoutStyle = styled.div`
   .layout-list {
     margin-bottom: 15px;
     padding: 0 8px;

+ 7 - 4
packages/builder/src/components/globalBtnList/index.js

@@ -2,7 +2,6 @@ import React from 'react'
 import { GLOBAL_BTN_ICON_URL } from '../../configs/theme'
 import { wrapSubmitSchema, CustomIcon } from '../../utils/util'
 import merge from 'lodash.merge'
-import { Button } from '@alifd/next'
 
 export default (props) => {
   const {
@@ -13,7 +12,8 @@ export default (props) => {
     changeCodeMode: _changeCodeMode,
     globalButtonList,
     showPreviewBtn,
-    showSourceCodeBtn
+    showSourceCodeBtn,
+    UI
   } = props
 
   // 获取主题下的默认icon图片地址
@@ -102,10 +102,13 @@ export default (props) => {
     ) : null
 
     const originalBtn = (
-      <Button key={key} {...props}>
+      <UI.Button
+        key={key}
+        {...props}
+      >
         {customIconTpl}
         <span>{title}</span>
-      </Button>
+      </UI.Button>
     )
 
     return render

+ 18 - 3
packages/builder/src/components/preview/fieldMiddleware.js

@@ -1,7 +1,6 @@
 import React from 'react'
 import cls from 'classnames'
 import { registerFieldMiddleware } from '../../utils/baseForm'
-import Icon from '@alifd/next/lib/icon'
 
 export default (FormConsumer) => {
   const hasRegisted = window.__hasRegisted__ || false
@@ -50,7 +49,15 @@ export default (FormConsumer) => {
               className='preview-line-layer-layout'
               title='编辑'
             >
-              <Icon type='edit' size='small' />
+              {
+                React.createElement(Field, {
+                  'x-component': 'Icon',
+                  'x-props': {
+                    type: 'edit',
+                    size: 'small'
+                  }
+                })
+              }
             </a>
             <a
               className='preview-line-del'
@@ -63,7 +70,15 @@ export default (FormConsumer) => {
               }}
               title='删除'
             >
-              <Icon type='ashbin' size='small' />
+              {
+                React.createElement(Field, {
+                  'x-component': 'Icon',
+                  'x-props': {
+                    type: 'ashbin',
+                    size: 'small'
+                  }
+                })
+              }
             </a>
           </div>
         </div>

+ 5 - 9
packages/builder/src/components/preview/index.js

@@ -15,8 +15,8 @@ import { normalizeSchema } from '../../utils/lang'
 import { isEmptyObj, Header } from '../../utils/util'
 import pick from 'lodash.pick'
 import registerPreviewFieldMiddleware from './fieldMiddleware'
-import styled from 'styled-components'
-import previewStyle from './style'
+
+import PreviewStyle from './style'
 
 const { Consumer: FormConsumer, Provider: FormProvider } = React.createContext()
 
@@ -187,7 +187,7 @@ class Preview extends Component {
         : {}
 
     return (
-      <div
+      <PreviewStyle
         className={cls('col-card preview-card', this.props.className)}
         {...dragProps}
       >
@@ -196,7 +196,7 @@ class Preview extends Component {
           <p>组件过多时可下拉查看更多</p>
         </Header>
         <div className='preview-main'>{this.renderPreviewList()}</div>
-      </div>
+      </PreviewStyle>
     )
   }
 }
@@ -215,13 +215,9 @@ const mapDispatchToProps = dispatch => ({
   changeComponent: componentId => dispatch(changeComponent(componentId))
 })
 
-const StyledPreview = styled(Preview)`
-  ${previewStyle}
-`
-
 class StyledPreviewComp extends React.Component {
   render() {
-    return <StyledPreview {...this.props} />
+    return <Preview {...this.props} />
   }
 }
 

+ 3 - 1
packages/builder/src/components/preview/style.js

@@ -1,4 +1,6 @@
-export default `
+import styled from 'styled-components'
+
+export default styled.div`
   position: relative;
   height: 100%;
   .preview-main {

+ 24 - 14
packages/builder/src/components/props/accordList.js

@@ -1,16 +1,20 @@
 import React from 'react'
 import PropsSetting from './propsSetting'
-import { Accordion } from '@alifd/next'
-import { SchemaForm, Field } from './utils/baseForm'
+import { SchemaForm, Field } from '../../utils/baseForm'
 import defaultGlobalCfgList from '../../configs/supportGlobalCfgList'
 
 export default class extends React.Component {
-  state = {}
+  constructor(props) {
+    super(props)
+    this.state = {
+      accordionList: this.getAccordionList()
+    }
+  }
 
   componentDidMount() {
-    this.setState({
-      accordionList: this.getAccordionList()
-    })
+    // this.setState({
+    //   accordionList: this.getAccordionList()
+    // })
   }
 
   generateGlobalCfgList = () => {
@@ -33,6 +37,7 @@ export default class extends React.Component {
   // global config
   renderGlobalConfig = () => {
     const globalCfgList = this.generateGlobalCfgList()
+
     const content = (
       <SchemaForm
         onChange={value => {
@@ -59,6 +64,7 @@ export default class extends React.Component {
           <PropsSetting
             supportConfigList={this.props.supportConfigList}
             renderEngine={this.props.renderEngine}
+            UI={this.props.UI}
           />
         ),
         expanded: true
@@ -76,13 +82,17 @@ export default class extends React.Component {
   }
 
   render() {
-    return <Accordion
-      dataSource={this.state.accordionList}
-      onChange={(status, list) => {
-        this.setState({
-          accordionList: list
-        })
-      }}
-    />
+    const { Accordtion } = this.props.UI
+
+    return (
+      <Accordtion
+        dataSource={this.state.accordionList}
+        onChange={(status, list) => {
+          this.setState({
+            accordionList: list
+          })
+        }}
+      />
+    )
   }
 }

+ 2 - 1
packages/builder/src/components/props/defaultValueCascader/index.js

@@ -16,13 +16,14 @@ class defaultValueCascader extends Component {
   }
 
   render() {
-    const { fieldStore = {}, value } = this.props
+    const { fieldStore = {}, value, UI } = this.props
 
     return (
       <DefaultValueEditor
         store={fieldStore}
         value={value}
         onChange={this.handleFieldAttrChange()}
+        UI={UI}
       />
     )
   }

+ 8 - 4
packages/builder/src/components/props/editors/fieldAttrEditors/dataSourceEditor.js

@@ -1,10 +1,7 @@
 import React, { Component } from 'react'
-import { Form, Input, Radio, Tab } from '@alifd/next'
 import DataSourceEnum from './dataSourceEnum'
 import styled from 'styled-components'
 
-const { Group: RadioGroup } = Radio
-const { TabPane } = Tab
 const LABEL_COL = { fixedSpan: 4 }
 const STYLE_W = { width: 220 }
 const JSON_EXAMPLE = `
@@ -147,11 +144,18 @@ class DataSourceEditor extends Component {
   }
 
   render() {
-    const { fieldStore = {} } = this.props
+    const { fieldStore = {}, UI } = this.props
     const { dataSourceType, error } = this.state
     const xProps = fieldStore['x-props'] || {}
     const { requestOptions = {} } = xProps
     const { data } = requestOptions
+    const { Form, Input, Radio, Tab } = UI
+
+    const { Group: RadioGroup } = Radio
+    const { TabPane } = Tab
+
+    this.RadioGroup = RadioGroup
+    this.TabPane = TabPane
 
     const dataSource = [
       {

+ 10 - 9
packages/builder/src/components/props/editors/fieldAttrEditors/dataSourceEnum.js

@@ -1,6 +1,5 @@
 import React from 'react'
 import styled from 'styled-components'
-import { Input, Button, Icon } from '@alifd/next'
 
 class Component extends React.Component {
   state = {}
@@ -32,42 +31,44 @@ class Component extends React.Component {
 
   renderItemRow = (item = {}, idx) => {
     const { value = '', label = '' } = item
+    const { UI } = this.props
     return (
       <div className='source-row' key={`${idx}`}>
         <ul>
           <li>
             <label>字段名:</label>
-            <Input
+            <UI.Input
               defaultValue={label}
               onBlur={e => this.handleChangeItemRow(e, idx, 'label')}
             />
           </li>
           <li>
             <label>值:</label>
-            <Input
+            <UI.Input
               defaultValue={value}
               onBlur={e => this.handleChangeItemRow(e, idx, 'value')}
             />
           </li>
         </ul>
-        <Button
+        <UI.Button
           className='ashbin-btn'
           shape='text'
           onClick={() => this.handleDeleteItem(idx)}
         >
-          <Icon type='ashbin' />
-        </Button>
+          <UI.Icon type='ashbin' />
+        </UI.Button>
       </div>
     )
   }
 
   renderNewBtn() {
+    const { UI } = this.props
     return (
       <div className='source-btn-new'>
-        <Button size='small' onClick={this.handleAddNewItem}>
-          <Icon type='add' />
+        <UI.Button size='small' onClick={this.handleAddNewItem}>
+          <UI.Icon type='add' />
           添加
-        </Button>
+        </UI.Button>
       </div>
     )
   }

+ 2 - 3
packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/arrayDefaultEditor.js

@@ -1,5 +1,4 @@
 import React, { Component } from 'react'
-import { Checkbox } from '@alifd/next'
 import DefaultValueGenerator from './defaultValueGenerator'
 
 const ds = [
@@ -19,7 +18,7 @@ class Editor extends Component {
   }
 
   render() {
-    const { store } = this.props
+    const { store, UI } = this.props
     const { enums } = store
 
     return (
@@ -30,7 +29,7 @@ class Editor extends Component {
         customEditor={{
           specify: (
             <div>
-              <Checkbox.Group dataSource={enums || []} />
+              <UI.Checkbox.Group dataSource={enums || []} />
             </div>
           )
         }}

+ 1 - 2
packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/boolDefaultEditor.js

@@ -1,5 +1,4 @@
 import React, { Component } from 'react'
-import { Switch } from '@alifd/next'
 import DefaultValueGenerator from './defaultValueGenerator'
 
 const ds = [
@@ -20,7 +19,7 @@ class Editor extends Component {
         ds={ds}
         {...this.props}
         customEditor={{
-          specify: <Switch style={{ marginLeft: 20 }} />
+          specify: <this.props.UI.Switch style={{ marginLeft: 20 }} />
         }}
       />
     )

+ 4 - 4
packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/dateDefaultEditor.js

@@ -1,5 +1,4 @@
 import React, { Component } from 'react'
-import { NumberPicker, DatePicker } from '@alifd/next'
 import DefaultValueGenerator from './defaultValueGenerator'
 
 const ds = [
@@ -26,7 +25,7 @@ const ds = [
 ]
 
 const DatePickerDefault = props => (
-  <DatePicker
+  <props.UI.DatePicker
     {...props}
     onChange={(v, vStr) => {
       props.onChange(vStr)
@@ -40,6 +39,7 @@ const DatePickerDefault = props => (
 
 class Editor extends Component {
   render() {
+    const { UI } = this.props
     return (
       <DefaultValueGenerator
         flag='date'
@@ -48,7 +48,7 @@ class Editor extends Component {
         customEditor={{
           now: <div />,
           future: (
-            <NumberPicker
+            <UI.NumberPicker
               addonAfter='天'
               inputWidth={100}
               style={{
@@ -58,7 +58,7 @@ class Editor extends Component {
             />
           ),
           past: (
-            <NumberPicker
+            <UI.NumberPicker
               addonAfter='天'
               inputWidth={100}
               style={{

+ 1 - 2
packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/dateTimeDefaultEditor.js

@@ -1,5 +1,4 @@
 import React, { Component } from 'react'
-import { DatePicker } from '@alifd/next'
 import DefaultValueGenerator from './defaultValueGenerator'
 
 const ds = [
@@ -18,7 +17,7 @@ const ds = [
 ]
 
 const DatePickerDefault = props => (
-  <DatePicker
+  <props.UI.DatePicker
     showTime
     onChange={(v, vStr) => {
       props.onChange(vStr)

+ 2 - 3
packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/defaultValueGenerator.js

@@ -1,5 +1,4 @@
 import React, { Component } from 'react'
-import { Select, Input } from '@alifd/next'
 
 class DefaultValueGenerator extends Component {
   static defaultProps = {
@@ -64,7 +63,7 @@ class DefaultValueGenerator extends Component {
             verticalAlign: 'top'
           }}
         >
-          <Input
+          <this.props.UI.Input
             value={value}
             onChange={this.handleValueChange}
             placeholder='url上的key'
@@ -83,7 +82,7 @@ class DefaultValueGenerator extends Component {
     const { type, value, flag } = this.state
     return (
       <div>
-        <Select
+        <this.props.UI.Select
           value={type}
           dataSource={this.props.ds}
           onChange={this.handleValueTypeChange}

+ 6 - 5
packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/index.js

@@ -84,17 +84,18 @@ class DefaultValueEditor extends Component {
 
   getEditor = editorType => {
     const props = this.createEditorProps(editorType)
+    const { UI } = this.props
     switch (editorType) {
       case 'string':
-        return <StringDefaultEditor {...props} />
+        return <StringDefaultEditor {...props} UI={UI} />
       case 'array':
-        return <ArrayDefaultEditor {...props} />
+        return <ArrayDefaultEditor {...props} UI={UI} />
       case 'boolean':
-        return <BoolDefaultEditor {...props} />
+        return <BoolDefaultEditor {...props} UI={UI} />
       case 'month':
-        return <MonthDefaultEditor {...props} />
+        return <MonthDefaultEditor {...props} UI={UI} />
       case 'date':
-        return <DateDefaultEditor {...props} />
+        return <DateDefaultEditor {...props} UI={UI} />
       case 'daterange':
         return <DateRangeDefaultEditor {...props} />
       case 'time':

+ 8 - 8
packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/monthDefaultEditor.js

@@ -1,9 +1,6 @@
 import React, { Component } from 'react'
-import { NumberPicker, DatePicker } from '@alifd/next'
 import DefaultValueGenerator from './defaultValueGenerator'
 
-const { MonthPicker } = DatePicker
-
 const ds = [
   {
     label: '当前月',
@@ -27,8 +24,10 @@ const ds = [
   }
 ]
 
-const MonthPickerDefault = props => (
-  <MonthPicker
+const MonthPickerDefault = props => {
+  const { UI } = props
+  const { MonthPicker } = UI.DatePicker
+  return <MonthPicker
     onChange={(v, vStr) => {
       props.onChange(vStr)
     }}
@@ -37,10 +36,11 @@ const MonthPickerDefault = props => (
       marginLeft: 20
     }}
   />
-)
+}
 
 class Editor extends Component {
   render() {
+    const { UI } = this.props
     return (
       <DefaultValueGenerator
         flag='month'
@@ -49,7 +49,7 @@ class Editor extends Component {
         customEditor={{
           now: <div />,
           future: (
-            <NumberPicker
+            <UI.NumberPicker
               addonAfter='月'
               inputWidth={100}
               style={{
@@ -59,7 +59,7 @@ class Editor extends Component {
             />
           ),
           past: (
-            <NumberPicker
+            <UI.NumberPicker
               addonAfter='月'
               inputWidth={100}
               style={{

+ 3 - 2
packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/stringDefaultEditor.js

@@ -1,5 +1,4 @@
 import React, { Component } from 'react'
-import { Input } from '@alifd/next'
 import DefaultValueGenerator from './defaultValueGenerator'
 
 const ds = [
@@ -15,6 +14,8 @@ const ds = [
 
 class Editor extends Component {
   render() {
+    const { UI } = this.props
+
     return (
       <DefaultValueGenerator
         flag='string'
@@ -22,7 +23,7 @@ class Editor extends Component {
         {...this.props}
         customEditor={{
           specify: (
-            <Input
+            <UI.Input
               placeholder='请输入默认值'
               style={{
                 verticalAlign: 'top'

+ 5 - 8
packages/builder/src/components/props/fileSetting.js

@@ -1,11 +1,8 @@
 import React, { Component } from 'react'
-import { Feedback, Upload, Button } from '@alifd/next'
 import PropTypes from 'prop-types'
 import { SchemaForm, Field } from '../../utils/baseForm'
 import remove from 'lodash.remove'
 
-const Toast = Feedback.toast
-
 const showUploadListData = [
   { value: 'text', label: '文字' },
   { value: 'text-image', label: '图文' },
@@ -106,7 +103,7 @@ class fileSetting extends Component {
   }
 
   renderUploadComp() {
-    const { xprops = {} } = this.props
+    const { xprops = {}, UI } = this.props
     const { defaultFileList = [] } = xprops
     return (
       <div className='next-form'>
@@ -117,7 +114,7 @@ class fileSetting extends Component {
           <div className='next-form-item-control'>
             <div className='schema-inline-field'>
               <div className='schema-form-field'>
-                <Upload
+                <UI.Upload
                   name='file'
                   listType='text'
                   withCredentials
@@ -156,11 +153,11 @@ class fileSetting extends Component {
                     this.onChangeDefaultFile(newDefaultFileList)
                   }}
                   onError={() => {
-                    Toast.error('文件上传失败')
+                    this.props.UI.Toast.error('文件上传失败')
                   }}
                 >
-                  <Button>上传文件</Button>
-                </Upload>
+                  <UI.Button>上传文件</UI.Button>
+                </UI.Upload>
               </div>
             </div>
           </div>

+ 10 - 11
packages/builder/src/components/props/propsSetting.js

@@ -9,8 +9,8 @@ import {
 } from '../../actions'
 import { State } from 'react-powerplug'
 import { getCompDetailById, flatObj } from '../../utils/util'
-import styled from 'styled-components'
-import propsStyle from './style'
+
+import PropsStyle from './style'
 
 import FileSetting from './fileSetting'
 
@@ -109,7 +109,8 @@ class PropsSetting extends Component {
     const {
       initSchemaData = {},
       componentId = '',
-      componentProps = {}
+      componentProps = {},
+      UI
     } = this.props
 
     if (!componentId) {
@@ -135,7 +136,8 @@ class PropsSetting extends Component {
       ) {
         newXprops = {
           ...newXprops,
-          fieldStore: curComponentAttr
+          fieldStore: curComponentAttr,
+          UI
         }
       }
       finalSchema[configItem.name] = {
@@ -208,6 +210,7 @@ class PropsSetting extends Component {
       case 'upload':
         return (
           <FileSetting
+            UI={this.props.UI}
             xprops={curComponentAttr['x-props'] || {}}
             onChange={xprops => {
               this.props.editComponent(
@@ -227,10 +230,10 @@ class PropsSetting extends Component {
 
   render() {
     return (
-      <div className={this.props.className}>
+      <PropsStyle className={this.props.className}>
         {this.renderConfigList()}
         {this.renderOptions()}
-      </div>
+      </PropsStyle>
     )
   }
 }
@@ -245,13 +248,9 @@ const mapDispatchToProps = dispatch => ({
     dispatch(editComponent(id, propsData, containerId))
 })
 
-const StyledPropsSetting = styled(PropsSetting)`
-  ${propsStyle}
-`
-
 class StyledPropsSettingComp extends React.Component {
   render() {
-    return <StyledPropsSetting {...this.props} />
+    return <PropsSetting {...this.props} />
   }
 }
 

+ 3 - 1
packages/builder/src/components/props/style.js

@@ -1,4 +1,6 @@
-export default `
+import styled from 'styled-components'
+
+export default styled.div`
   .schema-form-container .schema-form-content > .schema-form-field {
     &.option-item {
       padding: 10px !important;

+ 100 - 0
packages/builder/src/demo/index.js

@@ -0,0 +1,100 @@
+import React from 'react'
+import ReactDOM from 'react-dom'
+import SchemaForm from '@uform/next'
+import Index from '../index'
+import {
+  Button,
+  Collapse,
+  Message,
+  Upload,
+  Input,
+  Select,
+  DatePicker,
+  Icon,
+  Checkbox,
+  NumberPicker
+} from '@alifd/next'
+
+// style
+import '@alifd/next/dist/next.css'
+
+const renderSchema = {}
+
+const props = {
+  UI: {
+    Button,
+    Accordtion: Collapse,
+    Toast: Message,
+    Upload,
+    Input,
+    Select,
+    Icon,
+    DatePicker,
+    Checkbox,
+    NumberPicker
+  },
+  // 主题: dark/light,默认dark
+  // themeStyle: 'light',
+  // 是否展示布局组件,默认为false
+  showLayoutField: true,
+  showPreviewBtn: true,
+  showSourceCodeBtn: true,
+  // 控制返回按钮点击事件
+  onBackBtnClick: () => {
+    alert('点击了返回')
+  },
+  // 额外全局按钮
+  globalButtonList: [
+    // {
+    //   key: 'submit',
+    //   title: '自定义保存',
+    //   render: (props) => {
+    //     return <Badge dot>{props.children}</Badge>
+    //   },
+    //   props: {
+    //     // loading: true,
+    //   },
+    // }, {
+    //   key: 'cancel',
+    //   title: '取消',
+    //   props: {
+    //     onClick: () => {
+    //       alert('点击取消');
+    //     }
+    //   },
+    // }
+  ],
+  // 是否展示全局配置
+  showGlobalCfg: true,
+  // 全局配置额外项
+  extraGlobalCfgList: [],
+  globalCfg: {},
+  supportFieldList: [],
+  // includeFieldListKeyList: ['input', 'select'],
+
+  // 渲染引擎
+  renderEngine: SchemaForm,
+
+  schema: renderSchema,
+  // onChange: (data) => {
+  //   console.info('index onChange data', data);
+  // },
+  onSubmit: data => {
+    console.info('index onSubmit data', data)
+  }
+}
+
+class Comp extends React.Component {
+  constructor(props) {
+    super(props)
+    this.state = {
+      schema: renderSchema
+    }
+  }
+
+  render() {
+    return <Index {...props} schema={this.state.schema} />
+  }
+}
+
+ReactDOM.render(<Comp />, document.getElementById('root'))

+ 0 - 2
packages/builder/src/reducers/componentId.js

@@ -2,8 +2,6 @@ export default (state = '', action) => {
   let newState = state
   const { data = {} } = action
   const _componentId = data.componentId || ''
-  // eslint-disable-next-line
-  let { id, propsData = {}, comp = {}, key } = data
 
   switch (action.type) {
     case 'CHANGE_COMPONENT':

+ 4 - 0
packages/builder/src/reducers/initSchemaData.js

@@ -135,6 +135,10 @@ export default (state = {}, action) => {
         newState.properties = _properties1
       }
 
+      if (!newState.type) {
+        newState.type = 'object'
+      }
+
       return newState
     case 'EDIT_COMPONENT':
       const _data_ =

+ 7 - 6
packages/builder/src/style.js

@@ -1,4 +1,6 @@
-export default `
+import styled from 'styled-components'
+
+export default styled.div`
   position: relative;
   min-width: 600px;
   overflow: hidden;
@@ -36,8 +38,7 @@ export default `
       top: 24px;
       width: 9px;
       height: 17px;
-      background: url('${props =>
-    props.theme.backIconUrl}') no-repeat center center;
+      background: url('${props => props.theme.backIconUrl}') no-repeat center center;
       background-size: 9px 17px;
     }
     &::after {
@@ -121,10 +122,10 @@ export default `
   .schema-form-container .next-form-top .next-form-item-label {
     margin-bottom: 0 !important;
   }
-  .next-accordion {
+  .next-accordion, .next-collapse {
     border: none;
   }
-  .next-accordion-section-title {
+  .next-accordion-section-title, .next-collapse-panel-title {
     background: none;
     user-select: none;
     color: ${props => props.theme.whiteColor};
@@ -151,7 +152,7 @@ export default `
       }
     }
   }
-  .next-accordion-section-content {
+  .next-accordion-section-content, .next-collapse-panel-content {
     background: none;
     .next-form-left .next-form-item-label, .next-radio-group .next-radio-label {
       color: ${props => props.theme.whiteColor};

+ 0 - 60
packages/builder/src/utils/comp.js

@@ -1,9 +1,5 @@
 import React from 'react'
-import PropTypes from 'prop-types'
 import styled from 'styled-components'
-import { NumberPicker, Dialog } from '@alifd/next'
-
-import { connect, registerFormFields } from './baseForm'
 
 /**
  * 分割线
@@ -55,62 +51,6 @@ export const CustomIcon = styled(props => (
     ${props => props.height || '15'}px;
 `
 
-/* eslint-disable */
-const colsDetail = connect()(
-  class ColsDetail extends React.Component {
-    static propTypes = {
-      value: PropTypes.arrayOf(PropTypes.any),
-      onChange: PropTypes.func
-    }
-
-    handleChange = (idx, val) => {
-      if (!val) {
-        Dialog.alert('请确保列宽是有效整数')
-        return false
-      }
-      const { onChange, value } = this.props
-      let newValue = [...value]
-      const diff = val - newValue[idx]
-
-      if (diff >= newValue[newValue.length - 1]) {
-        Dialog.alert('请确保4列宽度加起来等于24')
-        return false
-      }
-
-      newValue = newValue.map((_val, i) => {
-        if (i === idx) {
-          return val
-        }
-        if (i === newValue.length - 1) {
-          return _val - diff
-        }
-        if (i < idx) {
-          return _val
-        }
-        return _val
-      })
-
-      onChange(newValue)
-    }
-
-    render() {
-      const { value = [] } = this.props
-      return value.map((item, idx) => (
-        <NumberPicker
-          key={idx}
-          value={item}
-          onChange={val => this.handleChange(idx, val)}
-        />
-      ))
-    }
-  }
-)
-/* eslint-enable */
-
-registerFormFields({
-  colsDetail
-})
-
 export default {
   Divider,
   Header,

+ 0 - 23
packages/builder/src/utils/lang.js

@@ -17,29 +17,6 @@ export const isNum = isType('Number')
 
 export const isIter = obj => isArr(obj) || isObj(obj)
 
-export const { hasOwnProperty } = Object.prototype
-
-export const hasOwn = (target, key) => hasOwnProperty.call(target, key)
-
-export const each = (target, fn) => {
-  if (!isFn(fn)) return
-  if (isArr(target)) {
-    for (let i = 0, len = target.length; i < len; i++) {
-      if (fn(target[i], i) === false) {
-        break
-      }
-    }
-  } else {
-    for (const key in target) {
-      if (hasOwn(target, key)) {
-        if (fn(target[key], key) === false) {
-          break
-        }
-      }
-    }
-  }
-}
-
 const replaceSingleDefault = v => {
   if (!isFlagValue(v)) return v
 

+ 1 - 1
packages/core/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@uform/core",
-  "version": "0.1.0-alpha.10",
+  "version": "0.1.0-alpha.12",
   "license": "MIT",
   "main": "lib",
   "repository": {

+ 2 - 2
packages/next/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@uform/next",
-  "version": "0.1.0-alpha.11",
+  "version": "0.1.0-alpha.12",
   "license": "MIT",
   "main": "lib",
   "repository": {
@@ -20,7 +20,7 @@
   },
   "dependencies": {
     "@alifd/next": "^1.13.1",
-    "@uform/react": "^0.1.0-alpha.11",
+    "@uform/react": "^0.1.0-alpha.12",
     "@uform/utils": "^0.1.0-alpha.10",
     "classnames": "^2.2.6",
     "moveto": "^1.7.4",

+ 2 - 2
packages/react/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@uform/react",
-  "version": "0.1.0-alpha.11",
+  "version": "0.1.0-alpha.12",
   "license": "MIT",
   "main": "lib",
   "repository": {
@@ -19,7 +19,7 @@
     "react-dom": ">=16.8.0"
   },
   "dependencies": {
-    "@uform/core": "^0.1.0-alpha.10",
+    "@uform/core": "^0.1.0-alpha.12",
     "@uform/utils": "^0.1.0-alpha.10",
     "@uform/validator": "^0.1.0-alpha.10",
     "pascal-case": "^2.0.1",