├── .gitignore ├── dist └── my-app │ ├── .storybook │ ├── preview.js │ └── main.js │ ├── src │ ├── setupTests.js │ ├── reportWebVitals.js │ └── index.js │ ├── config │ ├── webpack │ │ └── persistentCache │ │ │ └── createEnvironmentHash.js │ ├── jest │ │ ├── cssTransform.js │ │ ├── babelTransform.js │ │ └── fileTransform.js │ ├── getHttpsConfig.js │ ├── paths.js │ ├── modules.js │ ├── env.js │ ├── webpackDevServer.config.js │ └── webpack.config.js │ ├── .gitignore │ ├── scripts │ ├── test.js │ ├── start.js │ └── build.js │ ├── README.md │ └── package.json ├── .eslintrc.js ├── index.js ├── lib ├── template │ ├── Component.stories.js │ ├── App.js │ ├── ClassPage.js │ ├── ClassComponent.js │ ├── ExtendJS.js │ └── DS.js ├── Template.js ├── ExtendJS.js ├── Html.js ├── File.js ├── Plugin.js └── parse │ ├── ParseCommon.js │ ├── ParseCode.js │ ├── ParseRegex.js │ ├── ParseRender.js │ └── ParseOverride.js ├── package.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store -------------------------------------------------------------------------------- /dist/my-app/.storybook/preview.js: -------------------------------------------------------------------------------- 1 | export const parameters = { 2 | actions: { argTypesRegex: "^on[A-Z].*" }, 3 | controls: { 4 | matchers: { 5 | color: /(background|color)$/i, 6 | date: /Date$/, 7 | }, 8 | }, 9 | } -------------------------------------------------------------------------------- /dist/my-app/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /dist/my-app/config/webpack/persistentCache/createEnvironmentHash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { createHash } = require('crypto'); 3 | 4 | module.exports = env => { 5 | const hash = createHash('md5'); 6 | hash.update(JSON.stringify(env)); 7 | 8 | return hash.digest('hex'); 9 | }; 10 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "standard" 9 | ], 10 | "parserOptions": { 11 | "ecmaVersion": 12, 12 | "sourceType": "module" 13 | }, 14 | "rules": { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const Plugin = require('./lib/Plugin.js') 2 | 3 | // executed by electron's node.js 4 | module.exports = { 5 | async saveToFile (data, lib) { 6 | await Plugin.syncAppFiles(data.folder) 7 | await Plugin.syncStaticFolders(data) 8 | Plugin.syncIndexHtml(data.folder) 9 | Plugin.syncJsCode(data, lib) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /dist/my-app/.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "stories": [ 3 | "../src/**/*.stories.mdx", 4 | "../src/**/*.stories.@(js|jsx|ts|tsx)" 5 | ], 6 | "addons": [ 7 | "@storybook/addon-links", 8 | "@storybook/addon-essentials" 9 | ], 10 | "staticDirs": [ 11 | "../public" 12 | ], 13 | "framework": "@storybook/react" 14 | } -------------------------------------------------------------------------------- /lib/template/Component.stories.js: -------------------------------------------------------------------------------- 1 | import CLASSNAME from '../FILEPATH' 2 | import * as css from '../../public/css/compiled/style.css' // eslint-disable-line 3 | 4 | export default { 5 | title: 'Desech/CLASSNAME', 6 | component: CLASSNAME 7 | } 8 | 9 | export const CLASSNAMEStory = (args) => 10 | CLASSNAMEStory.storyName = 'CLASSNAME' 11 | -------------------------------------------------------------------------------- /lib/Template.js: -------------------------------------------------------------------------------- 1 | const File = require('./File.js') 2 | 3 | module.exports = { 4 | getProjectFile (folder, file) { 5 | const destFile = File.resolve(folder, file) 6 | return File.readFile(destFile) 7 | }, 8 | 9 | getTemplate (template) { 10 | const file = File.resolve(__dirname, 'template', template) 11 | return File.readFile(file) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /dist/my-app/config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /dist/my-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /lib/template/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { BrowserRouter, Routes, Route } from 'react-router-dom' 3 | // desech - start import block 4 | // desech - end import block 5 | 6 | export default function App () { 7 | return ( 8 | 9 | 10 | {/* desech - start router block */} 11 | {/* desech - end router block */} 12 | 13 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /dist/my-app/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /dist/my-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import reportWebVitals from './reportWebVitals'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ); 12 | 13 | // If you want to start measuring performance in your app, pass a function 14 | // to log results (for example: reportWebVitals(console.log)) 15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 16 | reportWebVitals(); 17 | -------------------------------------------------------------------------------- /lib/template/ClassPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | // desech - start import block 3 | // desech - end import block 4 | 5 | /** 6 | * A desech page 7 | **/ 8 | export default class CLASSNAME extends React.Component { 9 | render () { 10 | const d = DS.getDesechData(this.props, this.getComponentData()) // eslint-disable-line 11 | // desech - start render block 12 | // desech - end render block 13 | return render 14 | } 15 | 16 | getComponentData () { 17 | // desech - start data block 18 | // desech - end data block 19 | return data 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /dist/my-app/config/jest/babelTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const babelJest = require('babel-jest').default; 4 | 5 | const hasJsxRuntime = (() => { 6 | if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') { 7 | return false; 8 | } 9 | 10 | try { 11 | require.resolve('react/jsx-runtime'); 12 | return true; 13 | } catch (e) { 14 | return false; 15 | } 16 | })(); 17 | 18 | module.exports = babelJest.createTransformer({ 19 | presets: [ 20 | [ 21 | require.resolve('babel-preset-react-app'), 22 | { 23 | runtime: hasJsxRuntime ? 'automatic' : 'classic', 24 | }, 25 | ], 26 | ], 27 | babelrc: false, 28 | configFile: false, 29 | }); 30 | -------------------------------------------------------------------------------- /lib/template/ClassComponent.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | // desech - start import block 4 | // desech - end import block 5 | 6 | /** 7 | * A desech component 8 | **/ 9 | export default class CLASSNAME extends React.Component { 10 | render () { 11 | const d = DS.getDesechData(this.props, this.getComponentData()) // eslint-disable-line 12 | // desech - start render block 13 | // desech - end render block 14 | return render 15 | } 16 | 17 | getComponentData () { 18 | // desech - start data block 19 | // desech - end data block 20 | return data 21 | } 22 | } 23 | 24 | CLASSNAME.propTypes = { 25 | // desech - start props block 26 | // desech - end props block 27 | } 28 | -------------------------------------------------------------------------------- /lib/ExtendJS.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | toPascalCase (string) { 3 | return string.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, match => { 4 | if (+match === 0) return '' 5 | return match.toUpperCase() 6 | }).replace(/\W/g, '') 7 | }, 8 | 9 | isEmpty (obj) { 10 | return !obj || (Object.keys(obj).length === 0 && obj.constructor === Object) 11 | }, 12 | 13 | unique (array) { 14 | return [...new Set(array)] 15 | }, 16 | 17 | removeFromArray (array, value) { 18 | const index = array.indexOf(value) 19 | if (index !== -1) array.splice(index, 1) 20 | }, 21 | 22 | splitByCharacter (string, delimiter, wrap1 = '(', wrap2 = ')') { 23 | // regex is /(?![^(]*\))/gi 24 | const regex = new RegExp(`${delimiter}(?![^${wrap1}]*\\${wrap2})`, 'gi') 25 | return string.split(regex) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "studio-react", 3 | "version": "2.0.1", 4 | "description": "React Desech Studio Plugin", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git@github.com:desech/studio-react.git" 9 | }, 10 | "homepage": "https://www.desech.com", 11 | "author": { 12 | "name": "Desech Studio SRL", 13 | "email": "contact@desech.com" 14 | }, 15 | "license": "ISC", 16 | "devDependencies": { 17 | "eslint": "^8.5.0", 18 | "eslint-config-standard": "^16.0.3", 19 | "eslint-plugin-import": "^2.25.3", 20 | "eslint-plugin-node": "^11.1.0", 21 | "eslint-plugin-promise": "^6.0.0", 22 | "eslint-plugin-standard": "^4.1.0" 23 | }, 24 | "desech": { 25 | "title": "React", 26 | "author": "Desech", 27 | "category": "exportCode", 28 | "url": "https://github.com/desech/studio-react", 29 | "requires": "2.0.1", 30 | "autoUpdate": true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Desech 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 | -------------------------------------------------------------------------------- /lib/template/ExtendJS.js: -------------------------------------------------------------------------------- 1 | export default class ExtendJS { 2 | static toPascalCase (string) { 3 | return string.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, match => { 4 | if (+match === 0) return '' 5 | return match.toUpperCase() 6 | }).replace(/\W/g, '') 7 | } 8 | 9 | static isEmpty (obj) { 10 | return !obj || (Object.keys(obj).length === 0 && obj.constructor === Object) 11 | } 12 | 13 | static objectFlip (obj) { 14 | const ret = {} 15 | Object.keys(obj).forEach(key => { 16 | ret[obj[key]] = key 17 | }) 18 | return ret 19 | } 20 | 21 | static mergeDeep (target, ...sources) { 22 | if (!sources.length) return target 23 | const source = sources.shift() 24 | if (typeof target === 'object' && typeof source === 'object') { 25 | for (const key in source) { 26 | if (typeof source[key] === 'object') { 27 | if (!target[key]) Object.assign(target, { [key]: {} }) 28 | this.mergeDeep(target[key], source[key]) 29 | } else { 30 | Object.assign(target, { [key]: source[key] }) 31 | } 32 | } 33 | } 34 | return this.mergeDeep(target, ...sources) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /dist/my-app/config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const camelcase = require('camelcase'); 5 | 6 | // This is a custom Jest transformer turning file imports into filenames. 7 | // http://facebook.github.io/jest/docs/en/webpack.html 8 | 9 | module.exports = { 10 | process(src, filename) { 11 | const assetFilename = JSON.stringify(path.basename(filename)); 12 | 13 | if (filename.match(/\.svg$/)) { 14 | // Based on how SVGR generates a component name: 15 | // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6 16 | const pascalCaseFilename = camelcase(path.parse(filename).name, { 17 | pascalCase: true, 18 | }); 19 | const componentName = `Svg${pascalCaseFilename}`; 20 | return `const React = require('react'); 21 | module.exports = { 22 | __esModule: true, 23 | default: ${assetFilename}, 24 | ReactComponent: React.forwardRef(function ${componentName}(props, ref) { 25 | return { 26 | $$typeof: Symbol.for('react.element'), 27 | type: 'svg', 28 | ref: ref, 29 | key: null, 30 | props: Object.assign({}, props, { 31 | children: ${assetFilename} 32 | }) 33 | }; 34 | }), 35 | };`; 36 | } 37 | 38 | return `module.exports = ${assetFilename};`; 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /dist/my-app/scripts/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'test'; 5 | process.env.NODE_ENV = 'test'; 6 | process.env.PUBLIC_URL = ''; 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on('unhandledRejection', err => { 12 | throw err; 13 | }); 14 | 15 | // Ensure environment variables are read. 16 | require('../config/env'); 17 | 18 | const jest = require('jest'); 19 | const execSync = require('child_process').execSync; 20 | let argv = process.argv.slice(2); 21 | 22 | function isInGitRepository() { 23 | try { 24 | execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' }); 25 | return true; 26 | } catch (e) { 27 | return false; 28 | } 29 | } 30 | 31 | function isInMercurialRepository() { 32 | try { 33 | execSync('hg --cwd . root', { stdio: 'ignore' }); 34 | return true; 35 | } catch (e) { 36 | return false; 37 | } 38 | } 39 | 40 | // Watch unless on CI or explicitly running all tests 41 | if ( 42 | !process.env.CI && 43 | argv.indexOf('--watchAll') === -1 && 44 | argv.indexOf('--watchAll=false') === -1 45 | ) { 46 | // https://github.com/facebook/create-react-app/issues/5210 47 | const hasSourceControl = isInGitRepository() || isInMercurialRepository(); 48 | argv.push(hasSourceControl ? '--watch' : '--watchAll'); 49 | } 50 | 51 | 52 | jest.run(argv); 53 | -------------------------------------------------------------------------------- /dist/my-app/config/getHttpsConfig.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const crypto = require('crypto'); 6 | const chalk = require('react-dev-utils/chalk'); 7 | const paths = require('./paths'); 8 | 9 | // Ensure the certificate and key provided are valid and if not 10 | // throw an easy to debug error 11 | function validateKeyAndCerts({ cert, key, keyFile, crtFile }) { 12 | let encrypted; 13 | try { 14 | // publicEncrypt will throw an error with an invalid cert 15 | encrypted = crypto.publicEncrypt(cert, Buffer.from('test')); 16 | } catch (err) { 17 | throw new Error( 18 | `The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}` 19 | ); 20 | } 21 | 22 | try { 23 | // privateDecrypt will throw an error with an invalid key 24 | crypto.privateDecrypt(key, encrypted); 25 | } catch (err) { 26 | throw new Error( 27 | `The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${ 28 | err.message 29 | }` 30 | ); 31 | } 32 | } 33 | 34 | // Read file and throw an error if it doesn't exist 35 | function readEnvFile(file, type) { 36 | if (!fs.existsSync(file)) { 37 | throw new Error( 38 | `You specified ${chalk.cyan( 39 | type 40 | )} in your env, but the file "${chalk.yellow(file)}" can't be found.` 41 | ); 42 | } 43 | return fs.readFileSync(file); 44 | } 45 | 46 | // Get the https config 47 | // Return cert files if provided in env, otherwise just true or false 48 | function getHttpsConfig() { 49 | const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env; 50 | const isHttps = HTTPS === 'true'; 51 | 52 | if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) { 53 | const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE); 54 | const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE); 55 | const config = { 56 | cert: readEnvFile(crtFile, 'SSL_CRT_FILE'), 57 | key: readEnvFile(keyFile, 'SSL_KEY_FILE'), 58 | }; 59 | 60 | validateKeyAndCerts({ ...config, keyFile, crtFile }); 61 | return config; 62 | } 63 | return isHttps; 64 | } 65 | 66 | module.exports = getHttpsConfig; 67 | -------------------------------------------------------------------------------- /dist/my-app/config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebook/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 13 | // "public path" at which the app is served. 14 | // webpack needs to know it to put the right