├── .gitignore ├── LICENSE ├── README.md ├── __tests__ ├── sketchtool.test.js └── test.sketch ├── config ├── plugin │ ├── babel.js │ ├── eslint.js │ ├── paths.js │ └── webpack.js └── webview │ ├── babel.js │ ├── env.js │ ├── eslint.js │ ├── paths.js │ ├── polyfills.js │ ├── webpack-dev-server.js │ ├── webpack-dev.js │ └── webpack-prod.js ├── docs ├── 404.md ├── _config.yml ├── _includes │ ├── docs-navigation.html │ ├── global-vars │ ├── head.html │ ├── home-features.html │ ├── home-hero.html │ ├── navigation.html │ ├── svgs │ │ └── logo.svg │ └── tutorials-navigation.html ├── _layouts │ ├── default.html │ ├── docs.html │ ├── home.html │ └── tutorials.html ├── _sass │ ├── colors.scss │ ├── components │ │ ├── code.scss │ │ └── navigation.scss │ ├── layouts │ │ ├── default.scss │ │ └── home.scss │ ├── mixins.scss │ └── reset.scss ├── assets │ ├── icomoon │ │ ├── fonts │ │ │ ├── icomoon.eot │ │ │ ├── icomoon.svg │ │ │ ├── icomoon.ttf │ │ │ └── icomoon.woff │ │ ├── selection.json │ │ └── style.css │ ├── images │ │ └── favicon.png │ └── styles │ │ ├── index.scss │ │ └── pygments │ │ └── borland.css ├── docs │ ├── build.md │ ├── install.md │ ├── setup │ │ ├── eslint.md │ │ ├── jest.md │ │ └── webpack.md │ ├── tests.md │ └── utils │ │ ├── core.md │ │ ├── fetch.md │ │ └── webview.md ├── index.md └── tutorials │ ├── advanced │ ├── downloads.md │ ├── guis-with-webviews.md │ ├── network-requests.md │ └── sketch-menu.md │ ├── basics │ └── plugin-entrypoints.md │ └── index.md ├── package.json ├── scripts ├── build.js ├── plugin │ ├── build.js │ └── bundle.js ├── start-webview.js ├── start.js ├── utils │ ├── build.js │ ├── fox.js │ ├── sketchtool.js │ └── todos.js └── webview │ ├── build-bak.js │ └── build.js ├── src ├── docs │ └── assets │ │ ├── sketch-plugin-boilerplate-logo-reverse.svg │ │ ├── sketch-plugin-boilerplate-logo.sketch │ │ └── sketch-plugin-boilerplate-logo.svg ├── frameworks │ └── .gitkeep ├── plugin │ ├── index.js │ ├── manifest.json │ └── utils │ │ ├── core.js │ │ ├── formatter.js │ │ └── webview │ │ ├── index.js │ │ ├── panel.js │ │ ├── webview.js │ │ └── window.js └── webview │ ├── assets │ └── sketch-logo.svg │ ├── index.html │ ├── js │ ├── actions │ │ └── bridge.js │ ├── app.js │ ├── components │ │ └── .gitkeep │ ├── index.js │ ├── reducers │ │ ├── bridge.js │ │ └── index.js │ ├── store.js │ └── utils │ │ └── sketch.js │ └── scss │ ├── _colors.scss │ ├── _fonts.scss │ └── index.scss └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Node modules 2 | node_modules/ 3 | 4 | # Build folder 5 | Contents/ 6 | 7 | # Ignore knowledge base for now until cleaned up :D 8 | knowledge/ 9 | 10 | # Docs files 11 | docs/.sass-cache 12 | docs/_site -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Julian Burr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![Image](http://dev.burrdesign.de/sketch-plugin-boilerplate-logo-20170702.svg) 3 | 4 |
5 |
6 | 7 | # Sketch Plugin Boilerplate 8 | 9 | The Sketch Plugin Boilerplate is targeted at JS Developers, who want to create awesome plugins for [Sketch](https://sketchapp.com), but don't want to have to go through all the Obj C frustrations to be able to do more complex stuff, like building GUIs, handling HTTP requests, etc. 10 | 11 | I tried to use as little Obj C as possible, again, this is supposed to be a helpful starting point for JS developers. 12 | 13 |
14 | 15 | > This is meant as a starting point, so clone the repo, modify anything and everything to your needs and build an awesome Sketch plugin 16 | 17 |
18 | 19 | ## Why 20 | Sketch is awesome. And they provided us an awesome API to develop plugins. But as a JS Developer, there are still many things that are quiet hard to achieve. The main problem I keep hearing of is the ability to build custom user interfaces (GUIs), which requires some knowledge of how web views work in Obj C, how you can communicate with them and then **a lot of** boilerplate. 21 | 22 | This repo should make it easier and faster to start new projects :) 23 | 24 | 25 | ## Getting started 26 | 27 | ```bash 28 | # Change to plugin folder 29 | cd ~/Library/Application Support/com.bohemiancoding.sketch3/Plugins/ 30 | 31 | # Clone repo (as .sketchplugin!) 32 | git clone https://github.com/julianburr/sketch-plugin-boilerplate.git sketch-plugin-boilerplate.sketchplugin 33 | cd sketch-plugin-boilerplate.sketchplugin 34 | 35 | # Install dependecies 36 | yarn install 37 | 38 | # ...and create a first build 39 | yarn build 40 | ``` 41 | 42 | ... and you are ready to go :) 43 | 44 | 45 | ## Commands / Scripts 46 | 47 | ```bash 48 | # Build and watch plugin (🔥, no need to run build every time to see changes in Sketch!) 49 | yarn start 50 | 51 | # Build and watch webview(s) in browser 52 | yarn start:webview 53 | 54 | # Compile everything into correct folder structure to use it in Sketch 55 | yarn build 56 | 57 | # Run eslint --fix on the source directory 58 | yarn lint-fix 59 | 60 | # Run Jest tests (needs Sketch / sketchtool installed locally) 61 | yarn test 62 | ``` 63 | 64 | For the rest, see `package.json` 65 | 66 | 67 | ## Folder structure 68 | 69 | **Why is the folder structure as it is** 70 | 71 | The build structure follows [Sketch's guidelines](http://developer.sketchapp.com/introduction/plugin-bundles/) of how your plugin has to be structured. This makes development so much easier. All you have to do is to create the repo in your Sketch plugin folder (usually `~/Library/Application Support/com.bohemiancoding.sketch3/Plugins/`) and start coding. The watch and build scripts automatically put everything in the right place so you see the changes immediately in Sketch :) 72 | 73 | ```bash 74 | └─── /__tests__ # Jest tests and assets (e.g. Sketch files, etc) that are used for test scenarios 75 | └─── /config # Here you will find all necessary configurations, feel free to adjust them to your needs! :) 76 | │ 77 | └─── /Contents # This is the build folder that Sketch reads 78 | │ └─── /Resources # Assets, frameworks, etc. 79 | │ └─── /Sketch # plugin.js and manifest.json have to be here 80 | │ 81 | └─── /scripts # The npm scripts, also feel free to change to your needs, this is a boilerplate, not an end product! 82 | │ └─── /plugin # For `yarn *:plugin` scripts 83 | │ └─── /webview 84 | │ 85 | └─── /src # Source code, split into the different parts of your plugin 86 | │ └─── /framework # Any xcode cocoa frameworks you want to load into your plugin 87 | │ └─── /plugin # The plugins JS source code 88 | │ │ └─── index.js # By default, this will be used to bundle your production plugin.js file 89 | │ └─── /webview # The source for possible web views 90 | │ 91 | └─── package.json # By default, the version of your package.json will be copied into the plugins manifest.json 92 | ``` 93 | 94 | ## Documentation 95 | 96 | *In progress* 97 | 98 | ## Roadmap / Todos 99 | 100 | - [ ] Create useful documentation (integrated into a simple github.io page) 101 | - [ ] Create tutorials for JS developers to get started with Sketch plugins 102 | - [x] ~~Implement testing ([Jest](https://facebook.github.io/jest/)?)~~ *- Note: using [sketchtool](https://www.sketchapp.com/tool/) for accessing Sketch files and running plugin commands, however using own util functions, since the [node package](https://github.com/marekhrabe/sketchtool) does not seem to be maintained anymore (currently at version 39.x)* 103 | - [x] ~~Migrate the webview build from webpack to rollup, so we only have one build system to care about~~ 104 | - [ ] Re-Implement `fetch` polyfill (see master before merge for old solution with cocoa framework and plugin action handler) 105 | - [x] ~~Try to get rid of cocoa framework if feasable~~ 106 | 107 | ## About Testing 108 | 109 | Testing is a bit of a question mark for Sketch plugins at the moment, as it's not that easy to unit test your plugin commands in the Sketch enviroment. I am currently working on a util library / plugin that lets you run and test Sketch commands with Jest or any similar JS unit testing framework. If you are interested, just hit me up, happy to share my progress on this and collaborate. -------------------------------------------------------------------------------- /__tests__/sketchtool.test.js: -------------------------------------------------------------------------------- 1 | var sketchtool = require('../scripts/utils/sketchtool'); 2 | 3 | var testFilePath = '__tests__/test.sketch'; 4 | 5 | if (!sketchtool.check()) { 6 | process.exit(1); 7 | } 8 | 9 | describe('Check if sketchtool is set up correctly', () => { 10 | test('Can execute sketchtool', () => { 11 | expect(sketchtool.exec('help')).toBeTruthy(); 12 | }); 13 | 14 | test('Sketch version can be retrieved', () => { 15 | var version = sketchtool.getVersion(); 16 | expect(version).toBeTruthy(); 17 | }); 18 | 19 | test('Can dump load sketch files', () => { 20 | var json = sketchtool.loadFile(testFilePath); 21 | }); 22 | 23 | test('Can read dumped json', () => { 24 | var json = sketchtool.loadFile(testFilePath); 25 | expect(json.objectID).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /__tests__/test.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julianburr/sketch-plugin-boilerplate/070d3e0e1363f8f2fe2a6e99ca7f9eff85e085e0/__tests__/test.sketch -------------------------------------------------------------------------------- /config/plugin/babel.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ['es2015', { 4 | modules: false 5 | }] 6 | ], 7 | 'plugins': [ 8 | 'transform-object-rest-spread', 9 | ['module-resolver', { 10 | alias: { 11 | utils: './src/plugin/utils' 12 | } 13 | }] 14 | ] 15 | } -------------------------------------------------------------------------------- /config/plugin/eslint.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['semistandard'], 3 | globals: { 4 | MSDocument: false, 5 | MSDocumentWindow: false, 6 | MSPage: false, 7 | MSSymbolInstance: false, 8 | MSSymbolMaster: false, 9 | MSTextLayer: false, 10 | NSAlert: false, 11 | NSApp: false, 12 | NSClassFromString: false, 13 | NSColor: false, 14 | NSData: false, 15 | NSDocument: false, 16 | NSDocumentController: false, 17 | NSFileManager: false, 18 | NSImage: false, 19 | NSJSONSerialization: false, 20 | NSMakeRect: false, 21 | NSMutableData: false, 22 | NSMutableURLRequest: false, 23 | NSSaveOperation: false, 24 | NSString: false, 25 | NSTextField: false, 26 | NSTextView: false, 27 | NSThread: false, 28 | NSTitledWindowMask: false, 29 | NSURL: false, 30 | NSURLRequest: false, 31 | NSUTF8StringEncoding: false, 32 | NSUserDefaults: false, 33 | NSView: false, 34 | NSViewHeightSizable: false, 35 | NSViewWidthSizable: false, 36 | NSWindow: false, 37 | NSWorkspace: false, 38 | WKWebView: false, 39 | WKWebViewConfiguration: false, 40 | Mocha: false, 41 | log: false, 42 | NSBackingStoreBuffered: false, 43 | NSPanel: false, 44 | NSResizableWindowMask: false, 45 | NSWindowStyleMaskClosable: false, 46 | SPBWebViewMessageHandler: false, 47 | SPBWebViewMessageUtils: false 48 | }, 49 | parser: 'babel-eslint', 50 | plugins: [ 51 | 'no-unused-vars-rest' 52 | ], 53 | rules: { 54 | eqeqeq: [ 55 | 0 56 | ] 57 | } 58 | } -------------------------------------------------------------------------------- /config/plugin/paths.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var fs = require('fs-extra'); 3 | 4 | var appDirectory = fs.realpathSync(process.cwd()); 5 | function resolveApp(relativePath) { 6 | return path.resolve(appDirectory, relativePath); 7 | } 8 | 9 | var src = resolveApp('src/plugin'); 10 | var frameworks = resolveApp('src/frameworks'); 11 | 12 | module.exports = { 13 | src, 14 | entry: resolveApp('src/plugin/index.js'), 15 | manifest: resolveApp('src/plugin/manifest.json'), 16 | build: resolveApp('Contents/Sketch'), 17 | bundle: resolveApp('sketch-plugin-boilerplate.sketchplugin'), 18 | frameworks, 19 | frameworksBuild: resolveApp('Contents/Resources/frameworks'), 20 | watch: [src, frameworks] 21 | }; 22 | -------------------------------------------------------------------------------- /config/plugin/webpack.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs-extra'); 2 | var path = require('path'); 3 | var chalk = require('chalk'); 4 | var webpack = require('webpack'); 5 | var objectAssign = require('object-assign'); 6 | 7 | var paths = require('./paths'); 8 | var babelConfig = require('./babel'); 9 | 10 | var isDevelopment = process.env.NODE_ENV !== 'production'; 11 | 12 | babelConfig = objectAssign(babelConfig, { 13 | cacheDirectory: isDevelopment 14 | }); 15 | 16 | module.exports = { 17 | entry: paths.entry, 18 | output: { 19 | path: paths.build, 20 | filename: 'plugin.js', 21 | library: 'handlers', 22 | libraryTarget: 'var' 23 | }, 24 | module: { 25 | loaders: [ 26 | { 27 | test: /\.(js|jsx)$/, 28 | include: paths.src, 29 | exclude: /(node_modules)/, 30 | use: { 31 | loader: 'babel-loader', 32 | options: babelConfig 33 | } 34 | } 35 | ] 36 | }, 37 | plugins: [ 38 | new webpack.IgnorePlugin(/^sketch\/[a-z]+$/), 39 | new webpack.IgnorePlugin(/^sketch$/) 40 | ] 41 | }; 42 | -------------------------------------------------------------------------------- /config/webview/babel.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['react-app'], 3 | plugins: [ 4 | 'transform-object-rest-spread', 5 | 'transform-decorators-legacy', 6 | ['module-resolver', { 7 | alias: { 8 | webview: './src/webview', 9 | components: './src/webview/js/components', 10 | actions: './src/webview/js/actions', 11 | reducers: './src/webview/js/reducers', 12 | utils: './src/webview/js/utils', 13 | assets: './src/webview/assets', 14 | styles: './src/webview/scss' 15 | } 16 | }] 17 | ], 18 | cacheDirectory: true 19 | } 20 | -------------------------------------------------------------------------------- /config/webview/env.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra'); 2 | const path = require('path'); 3 | const paths = require('./paths'); 4 | 5 | // Make sure that including paths.js after env.js will read .env variables. 6 | delete require.cache[require.resolve('./paths')]; 7 | 8 | const NODE_ENV = process.env.NODE_ENV; 9 | if (!NODE_ENV) { 10 | throw new Error( 11 | 'The NODE_ENV environment variable is required but was not specified.' 12 | ); 13 | } 14 | 15 | // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use 16 | var dotenvFiles = [ 17 | `${paths.dotenv}.${NODE_ENV}.local`, 18 | `${paths.dotenv}.${NODE_ENV}`, 19 | // Don't include `.env.local` for `test` environment 20 | // since normally you expect tests to produce the same 21 | // results for everyone 22 | NODE_ENV !== 'test' && `${paths.dotenv}.local`, 23 | paths.dotenv, 24 | ].filter(Boolean); 25 | 26 | // Load environment variables from .env* files. Suppress warnings using silent 27 | // if this file is missing. dotenv will never modify any environment variables 28 | // that have already been set. 29 | // https://github.com/motdotla/dotenv 30 | dotenvFiles.forEach(dotenvFile => { 31 | if (fs.existsSync(dotenvFile)) { 32 | require('dotenv').config({ 33 | path: dotenvFile, 34 | }); 35 | } 36 | }); 37 | 38 | // We support resolving modules according to `NODE_PATH`. 39 | // This lets you use absolute paths in imports inside large monorepos: 40 | // https://github.com/facebookincubator/create-react-app/issues/253. 41 | // It works similar to `NODE_PATH` in Node itself: 42 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders 43 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. 44 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims. 45 | // https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421 46 | // We also resolve them to make sure all tools using them work consistently. 47 | const appDirectory = fs.realpathSync(process.cwd()); 48 | process.env.NODE_PATH = (process.env.NODE_PATH || '') 49 | .split(path.delimiter) 50 | .filter(folder => folder && !path.isAbsolute(folder)) 51 | .map(folder => path.resolve(appDirectory, folder)) 52 | .join(path.delimiter); 53 | 54 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be 55 | // injected into the application via DefinePlugin in Webpack configuration. 56 | const REACT_APP = /^REACT_APP_/i; 57 | 58 | function getClientEnvironment(publicUrl) { 59 | const raw = Object.keys(process.env) 60 | .filter(key => REACT_APP.test(key)) 61 | .reduce( 62 | (env, key) => { 63 | env[key] = process.env[key]; 64 | return env; 65 | }, 66 | { 67 | // Useful for determining whether we’re running in production mode. 68 | // Most importantly, it switches React into the correct mode. 69 | NODE_ENV: process.env.NODE_ENV || 'development', 70 | // Useful for resolving the correct path to static assets in `public`. 71 | // For example, . 72 | // This should only be used as an escape hatch. Normally you would put 73 | // images into the `src` and `import` them in code to get their paths. 74 | PUBLIC_URL: publicUrl, 75 | } 76 | ); 77 | // Stringify all values so we can feed into Webpack DefinePlugin 78 | const stringified = { 79 | 'process.env': Object.keys(raw).reduce((env, key) => { 80 | env[key] = JSON.stringify(raw[key]); 81 | return env; 82 | }, {}), 83 | }; 84 | 85 | return { raw, stringified }; 86 | } 87 | 88 | module.exports = getClientEnvironment; -------------------------------------------------------------------------------- /config/webview/eslint.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ['react'], 3 | extends: ['semistandard'], 4 | env: { 5 | es6: true 6 | }, 7 | parser: 'babel-eslint', 8 | parserOptions: { 9 | sourceType: 'module', 10 | ecmaVersion: 6, 11 | ecmaFeatures: { 12 | impliedStrict: true, 13 | experimentalObjectRestSpread: true, 14 | jsx: true 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /config/webview/paths.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var fs = require('fs-extra'); 3 | 4 | var appDirectory = fs.realpathSync(process.cwd()); 5 | function resolveApp(relativePath) { 6 | return path.resolve(appDirectory, relativePath); 7 | } 8 | 9 | var nodePaths = (process.env.NODE_PATH || '') 10 | .split(process.platform === 'win32' ? ';' : ':') 11 | .filter(Boolean) 12 | .filter(folder => !path.isAbsolute(folder)) 13 | .map(resolveApp); 14 | 15 | module.exports = { 16 | src: resolveApp('src/webview/'), 17 | build: resolveApp('Contents/Resources/webview/'), 18 | buildBundleJs: 'Contents/Resources/webview/js/index.js', 19 | indexHtml: resolveApp('src/webview/index.html'), 20 | indexJs: resolveApp('src/webview/js/index.js'), 21 | packageJson: resolveApp('package.json'), 22 | yarnLockFile: resolveApp('yarn.lock'), 23 | appNodeModules: resolveApp('node_modules'), 24 | ownNodeModules: resolveApp('node_modules'), 25 | nodePaths: nodePaths, 26 | yarnLockFile: resolveApp('yarn.lock'), 27 | homepage: './' 28 | }; -------------------------------------------------------------------------------- /config/webview/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // fetch() polyfill for making API calls. 10 | require('whatwg-fetch'); 11 | 12 | // Object.assign() is commonly used with React. 13 | // It will use the native implementation if it's present and isn't buggy. 14 | Object.assign = require('object-assign'); 15 | -------------------------------------------------------------------------------- /config/webview/webpack-dev-server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const errorOverlayMiddleware = require('react-error-overlay/middleware'); 4 | const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware'); 5 | const config = require('./webpack-dev'); 6 | const paths = require('./paths'); 7 | 8 | const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; 9 | const host = process.env.HOST || '0.0.0.0'; 10 | 11 | module.exports = function(proxy, allowedHost) { 12 | return { 13 | // WebpackDevServer 2.4.3 introduced a security fix that prevents remote 14 | // websites from potentially accessing local content through DNS rebinding: 15 | // https://github.com/webpack/webpack-dev-server/issues/887 16 | // https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a 17 | // However, it made several existing use cases such as development in cloud 18 | // environment or subdomains in development significantly more complicated: 19 | // https://github.com/facebookincubator/create-react-app/issues/2271 20 | // https://github.com/facebookincubator/create-react-app/issues/2233 21 | // While we're investigating better solutions, for now we will take a 22 | // compromise. Since our WDS configuration only serves files in the `public` 23 | // folder we won't consider accessing them a vulnerability. However, if you 24 | // use the `proxy` feature, it gets more dangerous because it can expose 25 | // remote code execution vulnerabilities in backends like Django and Rails. 26 | // So we will disable the host check normally, but enable it if you have 27 | // specified the `proxy` setting. Finally, we let you override it if you 28 | // really know what you're doing with a special environment variable. 29 | disableHostCheck: 30 | !proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true', 31 | // Enable gzip compression of generated files. 32 | compress: true, 33 | // Silence WebpackDevServer's own logs since they're generally not useful. 34 | // It will still show compile warnings and errors with this setting. 35 | clientLogLevel: 'none', 36 | // By default WebpackDevServer serves physical files from current directory 37 | // in addition to all the virtual build products that it serves from memory. 38 | // This is confusing because those files won’t automatically be available in 39 | // production build folder unless we copy them. However, copying the whole 40 | // project directory is dangerous because we may expose sensitive files. 41 | // Instead, we establish a convention that only files in `public` directory 42 | // get served. Our build script will copy `public` into the `build` folder. 43 | // In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%: 44 | // 45 | // In JavaScript code, you can access it with `process.env.PUBLIC_URL`. 46 | // Note that we only recommend to use `public` folder as an escape hatch 47 | // for files like `favicon.ico`, `manifest.json`, and libraries that are 48 | // for some reason broken when imported through Webpack. If you just want to 49 | // use an image, put it in `src` and `import` it from JavaScript instead. 50 | contentBase: paths.src, 51 | // By default files from `contentBase` will not trigger a page reload. 52 | watchContentBase: true, 53 | // Enable hot reloading server. It will provide /sockjs-node/ endpoint 54 | // for the WebpackDevServer client so it can learn when the files were 55 | // updated. The WebpackDevServer client is included as an entry point 56 | // in the Webpack development configuration. Note that only changes 57 | // to CSS are currently hot reloaded. JS changes will refresh the browser. 58 | hot: true, 59 | // It is important to tell WebpackDevServer to use the same "root" path 60 | // as we specified in the config. In development, we always serve from /. 61 | publicPath: config.build, 62 | // WebpackDevServer is noisy by default so we emit custom message instead 63 | // by listening to the compiler events with `compiler.plugin` calls above. 64 | quiet: true, 65 | // Reportedly, this avoids CPU overload on some systems. 66 | // https://github.com/facebookincubator/create-react-app/issues/293 67 | watchOptions: { 68 | ignored: /node_modules/, 69 | }, 70 | // Enable HTTPS if the HTTPS environment variable is set to 'true' 71 | https: protocol === 'https', 72 | host: host, 73 | overlay: false, 74 | historyApiFallback: { 75 | // Paths with dots should still use the history fallback. 76 | // See https://github.com/facebookincubator/create-react-app/issues/387. 77 | disableDotRule: true, 78 | }, 79 | public: allowedHost, 80 | proxy, 81 | setup(app) { 82 | // This lets us open files from the runtime error overlay. 83 | app.use(errorOverlayMiddleware()); 84 | // This service worker file is effectively a 'no-op' that will reset any 85 | // previous service worker registered for the same host:port combination. 86 | // We do this in development to avoid hitting the production cache if 87 | // it used the same host and port. 88 | // https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432 89 | app.use(noopServiceWorkerMiddleware()); 90 | }, 91 | }; 92 | }; 93 | -------------------------------------------------------------------------------- /config/webview/webpack-dev.js: -------------------------------------------------------------------------------- 1 | const autoprefixer = require('autoprefixer'); 2 | const path = require('path'); 3 | const webpack = require('webpack'); 4 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 5 | const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); 6 | 7 | const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); 8 | const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin'); 9 | const eslintFormatter = require('react-dev-utils/eslintFormatter'); 10 | const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); 11 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 12 | 13 | const getClientEnvironment = require('./env'); 14 | const paths = require('./paths'); 15 | 16 | // Webpack uses `publicPath` to determine where the app is being served from. 17 | // In development, we always serve from the root. This makes config easier. 18 | const publicPath = '/'; 19 | // `publicUrl` is just like `publicPath`, but we will provide it to our app 20 | // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript. 21 | // Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz. 22 | const publicUrl = ''; 23 | // Get environment variables to inject into our app. 24 | const env = getClientEnvironment(publicUrl); 25 | 26 | const cssLoader = { 27 | loader: require.resolve('css-loader'), 28 | options: { 29 | importLoaders: 1, 30 | } 31 | }; 32 | const babel = require('./babel'); 33 | 34 | const postCssLoader = { 35 | loader: require.resolve('postcss-loader'), 36 | options: { 37 | // Necessary for external CSS imports to work 38 | // https://github.com/facebookincubator/create-react-app/issues/2677 39 | ident: 'postcss', 40 | plugins: () => [ 41 | require('postcss-flexbugs-fixes'), 42 | autoprefixer({ 43 | browsers: [ 44 | '>1%', 45 | 'last 4 versions', 46 | 'Firefox ESR', 47 | 'not ie < 9', // React doesn't support IE8 anyway 48 | ], 49 | flexbox: 'no-2009', 50 | }), 51 | ], 52 | }, 53 | }; 54 | 55 | // This is the development configuration. 56 | // It is focused on developer experience and fast rebuilds. 57 | // The production configuration is different and lives in a separate file. 58 | 59 | 60 | module.exports = { 61 | // You may want 'eval' instead if you prefer to see the compiled output in DevTools. 62 | // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343. 63 | devtool: 'cheap-module-source-map', 64 | // These are the "entry points" to our application. 65 | // This means they will be the "root" imports that are included in JS bundle. 66 | // The first two entry points enable "hot" CSS and auto-refreshes for JS. 67 | entry: [ 68 | // Include an alternative client for WebpackDevServer. A client's job is to 69 | // connect to WebpackDevServer by a socket and get notified about changes. 70 | // When you save a file, the client will either apply hot updates (in case 71 | // of CSS changes), or refresh the page (in case of JS changes). When you 72 | // make a syntax error, this client will display a syntax error overlay. 73 | // Note: instead of the default WebpackDevServer client, we use a custom one 74 | // to bring better experience for Create React App users. You can replace 75 | // the line below with these two lines if you prefer the stock client: 76 | // require.resolve('webpack-dev-server/client') + '?/', 77 | // require.resolve('webpack/hot/dev-server'), 78 | require.resolve('react-dev-utils/webpackHotDevClient'), 79 | // We ship a few polyfills by default: 80 | require.resolve('./polyfills'), 81 | // Errors should be considered fatal in development 82 | require.resolve('react-error-overlay'), 83 | // Finally, this is your app's code: 84 | paths.indexJs, 85 | // We include the app code last so that if there is a runtime error during 86 | // initialization, it doesn't blow up the WebpackDevServer client, and 87 | // changing JS code would still trigger a refresh. 88 | ], 89 | output: { 90 | // Next line is not used in dev but WebpackDevServer crashes without it: 91 | path: paths.build, 92 | // Add /* filename */ comments to generated require()s in the output. 93 | pathinfo: true, 94 | // This does not produce a real file. It's just the virtual path that is 95 | // served by WebpackDevServer in development. This is the JS bundle 96 | // containing code from all our entry points, and the Webpack runtime. 97 | filename: 'static/js/bundle.js', 98 | // There are also additional JS chunk files if you use code splitting. 99 | chunkFilename: 'static/js/[name].chunk.js', 100 | // This is the URL that app is served from. We use "/" in development. 101 | publicPath: publicPath, 102 | // Point sourcemap entries to original disk location (format as URL on Windows) 103 | devtoolModuleFilenameTemplate: info => 104 | path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'), 105 | }, 106 | resolve: { 107 | // This allows you to set a fallback for where Webpack should look for modules. 108 | // We placed these paths second because we want `node_modules` to "win" 109 | // if there are any conflicts. This matches Node resolution mechanism. 110 | // https://github.com/facebookincubator/create-react-app/issues/253 111 | modules: ['node_modules', paths.appNodeModules].concat( 112 | // It is guaranteed to exist because we tweak it in `env.js` 113 | process.env.NODE_PATH.split(path.delimiter).filter(Boolean) 114 | ), 115 | // These are the reasonable defaults supported by the Node ecosystem. 116 | // We also include JSX as a common component filename extension to support 117 | // some tools, although we do not recommend using it, see: 118 | // https://github.com/facebookincubator/create-react-app/issues/290 119 | // `web` extension prefixes have been added for better support 120 | // for React Native Web. 121 | extensions: ['.web.js', '.js', '.json', '.web.jsx', '.jsx'], 122 | alias: { 123 | 124 | // Support React Native Web 125 | // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ 126 | 'react-native': 'react-native-web', 127 | }, 128 | plugins: [ 129 | // Prevents users from importing files from outside of src/ (or node_modules/). 130 | // This often causes confusion because we only process files within src/ with babel. 131 | // To fix this, we prevent you from importing files out of src/ -- if you'd like to, 132 | // please link the files into your node_modules/ and let module-resolution kick in. 133 | // Make sure your source files are compiled, as they will not be processed in any way. 134 | new ModuleScopePlugin(paths.src), 135 | ], 136 | }, 137 | module: { 138 | strictExportPresence: true, 139 | rules: [ 140 | // ** ADDING/UPDATING LOADERS ** 141 | // The "file" loader handles all assets unless explicitly excluded. 142 | // The `exclude` list *must* be updated with every change to loader extensions. 143 | // When adding a new loader, you must add its `test` 144 | // as a new entry in the `exclude` list for "file" loader. 145 | 146 | // "file" loader makes sure those assets get served by WebpackDevServer. 147 | // When you `import` an asset, you get its (virtual) filename. 148 | // In production, they would get copied to the `build` folder. 149 | { 150 | exclude: [ 151 | /\.html$/, 152 | /\.(js|jsx)$/, 153 | /\.css$/, 154 | /\.json$/, 155 | /\.bmp$/, 156 | /\.gif$/, 157 | /\.jpe?g$/, 158 | /\.png$/, 159 | ], 160 | loader: require.resolve('file-loader'), 161 | options: { 162 | name: 'static/media/[name].[hash:8].[ext]', 163 | }, 164 | }, 165 | // "url" loader works like "file" loader except that it embeds assets 166 | // smaller than specified limit in bytes as data URLs to avoid requests. 167 | // A missing `test` is equivalent to a match. 168 | { 169 | test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], 170 | loader: require.resolve('url-loader'), 171 | options: { 172 | limit: 10000, 173 | name: 'static/media/[name].[hash:8].[ext]', 174 | }, 175 | }, 176 | // Process JS with Babel. 177 | { 178 | test: /\.(js|jsx)$/, 179 | include: paths.src, 180 | loader: require.resolve('babel-loader'), 181 | options: babel 182 | }, 183 | 184 | // "postcss" loader applies autoprefixer to our CSS. 185 | // "css" loader resolves paths in CSS and adds assets as dependencies. 186 | { 187 | test: /\.scss$/, 188 | loader: ExtractTextPlugin.extract( 189 | Object.assign( 190 | { 191 | fallback: require.resolve('style-loader'), 192 | use: [ 193 | cssLoader, 194 | { 195 | loader: require.resolve('sass-loader') 196 | }, 197 | postCssLoader 198 | ] 199 | }, 200 | {} 201 | ) 202 | ), 203 | }, 204 | 205 | // "postcss" loader applies autoprefixer to our CSS. 206 | // "css" loader resolves paths in CSS and adds assets as dependencies. 207 | { 208 | test: /\.css$/, 209 | use: [ 210 | require.resolve('style-loader'), 211 | cssLoader, 212 | postCssLoader 213 | ], 214 | }, 215 | // ** STOP ** Are you adding a new loader? 216 | // Remember to add the new extension(s) to the "file" loader exclusion list. 217 | ], 218 | }, 219 | plugins: [ 220 | // Makes some environment variables available in index.html. 221 | // The public URL is available as %PUBLIC_URL% in index.html, e.g.: 222 | // 223 | // In development, this will be an empty string. 224 | new InterpolateHtmlPlugin(env.raw), 225 | // Generates an `index.html` file with the