fix(bundle): handle namespaces

This commit is contained in:
Sérgio Ramos 2018-03-29 14:43:30 +01:00 committed by Sérgio Ramos
parent 682f9db749
commit f73a9f4eca
20 changed files with 696 additions and 541 deletions

8
bundle/.env.js Normal file
View File

@ -0,0 +1,8 @@
'use strict';
const { homedir } = require('os');
const { join } = require('path');
const { SDC_KEY_PATH } = process.env;
process.env.SDC_KEY_PATH = SDC_KEY_PATH || join(homedir(), './.ssh/id_rsa');

View File

89
bundle/data/categories.js Normal file
View File

@ -0,0 +1,89 @@
module.exports = [
{
name: 'Compute',
services: [
{
name: 'VMs & Containers',
slug: 'instances',
description: 'Run VMs and bare metal containers'
}
]
},
{
name: 'Network',
services: [
{
name: 'VLANs',
slug: 'vlans',
description: 'Wire your application your way'
},
{
name: 'Subnets',
slug: 'subnets',
description: 'A network for everything'
},
{
name: 'Firewall Rules',
slug: 'firewall',
description: 'Control the bits coming and going'
}
]
},
{
name: 'Storage',
services: [
{
name: 'Triton Object Storage',
slug: 'object-storage',
description: 'Modern cloud object storage',
tags: ["'note'='was Manta'"]
},
{
name: 'S3 Compatibility Bridge',
slug: 's3-bridge',
description: 'Modern storage, legacy compability'
},
{
name: 'Triton Volumes',
slug: 'volumes',
description: 'Network filesystems for your apps',
tags: ["'is-new'='true'"]
}
]
},
{
name: 'Access',
services: [
{
name: 'Role Based Access Control',
slug: 'rbac',
description: 'Manage users within your account'
}
]
},
{
name: 'Help & Support',
services: [
{
name: 'Service Status',
slug: 'status',
description: 'Find out about the status of our services'
},
{
name: 'Contact Support',
slug: 'contact-support',
description: 'Chat to us via phone or email'
},
{
name: 'Support Plans',
slug: 'support-plans',
description: 'Write here about Support Plans'
},
{
name: 'Getting Started',
slug: 'getting-started',
description: 'Write here about Getting Started'
}
]
}
];

86
bundle/data/regions.js Normal file
View File

@ -0,0 +1,86 @@
module.exports = [
{
name: 'Ashburn, Virginia, USA',
continent: 'NORTH_AMERICA',
datacenters: [
{
name: 'us-east-1',
url: 'http://localhost'
},
{
name: 'us-east-2',
url: 'http://localhost'
},
{
name: 'us-east-3',
url: 'http://localhost'
}
]
},
{
name: 'Las Vegas, Nevada, USA',
continent: 'NORTH_AMERICA',
datacenters: [
{
name: 'us-sw-1',
url: 'http://localhost'
}
]
},
{
name: 'Emeryville, California, USA',
continent: 'NORTH_AMERICA',
datacenters: [
{
name: 'us-west-1',
url: 'http://localhost'
}
]
},
{
name: 'Amsterdam, Netherlands',
continent: 'EUROPE',
datacenters: [
{
name: 'us-ams-1',
url: 'http://localhost'
}
]
},
{
name: 'Singapore',
continent: 'ASIA',
datacenters: [
{
name: 'ap-sg-1',
url: 'http://localhost'
},
{
name: 'ap-sg-2',
url: 'http://localhost'
},
{
name: 'ap-sg-3',
url: 'http://localhost'
}
]
},
{
name: 'Seoul, South Korea',
continent: 'ASIA',
datacenters: [
{
name: 'ap-kr-1',
url: 'http://localhost'
},
{
name: 'ap-kr-2',
url: 'http://localhost'
},
{
name: 'ap-kr-3',
url: 'http://localhost'
}
]
}
];

72
bundle/index.js Normal file
View File

@ -0,0 +1,72 @@
// Requires .env.js file with the following exports:
// SDC_URL, SDC_KEY_ID, SDC_KEY_PATH
require('./.env.js');
const Main = require('apr-main');
const Hapi = require('hapi');
const H2O2 = require('h2o2');
const Execa = require('execa');
const Path = require('path');
const Fs = require('fs');
const { PORT = 4000 } = process.env;
const ROOT = Path.join(__dirname, 'src');
const calcPort = (i) => Number(PORT) + Number(i) + 1;
const namespaces = Fs.readdirSync(ROOT)
.filter(filename => /.js$/.test(filename))
.map(filename => filename.replace(/.js$/, ''))
.filter(filename => !['index', 'server'].includes(filename));
const routes = namespaces.map((namespace, i) => ({
method: '*',
path: `/${namespace}/{params*}`,
handler: {
proxy: {
uri: `{protocol}://0.0.0.0:${calcPort(i)}/${namespace}/{params}`
}
}
}));
namespaces.forEach((namespace, i) => {
const child = Execa('node', [namespace], {
cwd: ROOT,
cleanup: true,
env: Object.assign({}, process.env, {
PORT: calcPort(i),
PREFIX: namespace
})
});
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
});
Main(async () => {
const server = Hapi.server({
port: PORT,
routes: {
cors: {
origin: ['*'],
credentials: true,
additionalHeaders: ['Cookie', 'X-CSRF-Token']
}
},
debug: {
log: ['error'],
request: ['error']
}
});
await server.register({
plugin: H2O2
});
routes.map(route => server.route(route));
await server.start();
// eslint-disable-next-line no-console
console.log(`server started at http://0.0.0.0:${server.info.port}`);
});

View File

@ -4,8 +4,7 @@
"private": true,
"license": "MPL-2.0",
"scripts": {
"start": "UMD=1 NODE_ENV=development PORT=3069 REACT_APP_GQL_PORT=3069 REACT_APP_GQL_HOSTNAME=localhost REACT_APP_GQL_PROTOCOL=http node src/index.js",
"dev": "echo 0",
"dev": "NODE_ENV=development PORT=4000 node index.js",
"build:test": "echo 0",
"build:lib": "echo 0",
"build:bundle": "echo 0",
@ -16,14 +15,18 @@
"test:ci": "echo 0"
},
"dependencies": {
"apr-main": "^4.0.3",
"brule": "^3.1.0",
"cloudapi-gql": "^7.1.3",
"hapi": "^17.2.0",
"hapi-triton-auth": "^2.0.0",
"cloudapi-gql": "^7.1.4",
"execa": "^0.10.0",
"h2o2": "^8.0.1",
"hapi": "^17.3.1",
"hapi-triton-auth": "^2.0.1",
"hapi-webconsole-nav": "^1.1.1",
"inert": "^5.1.0",
"my-joy-navigation": "*",
"my-joy-images": "*",
"my-joy-instances": "*",
"my-joy-navigation": "*",
"rollover": "^1.0.0"
}
}

43
bundle/src/images.js Normal file
View File

@ -0,0 +1,43 @@
const Main = require('apr-main');
const CloudApiGql = require('cloudapi-gql');
const Url = require('url');
const Server = require('./server');
const {
PORT = 4003,
BASE_URL = `http://0.0.0.0:${PORT}`,
PREFIX = 'images',
DC_NAME,
SDC_URL,
SDC_KEY_PATH,
SDC_ACCOUNT,
SDC_KEY_ID
} = process.env;
const dcName = DC_NAME || Url.parse(SDC_URL).host.split('.')[0];
const keyPath = SDC_KEY_PATH;
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
const apiBaseUrl = SDC_URL;
Main(async () => {
const server = await Server({
PORT,
BASE_URL
});
await server.register({
plugin: CloudApiGql,
options: {
authStrategy: 'sso',
keyPath,
keyId,
apiBaseUrl,
dcName
},
routes: {
prefix: `/${PREFIX}`
}
});
await server.start();
});

View File

@ -1,101 +0,0 @@
'use strict';
const Brule = require('brule');
const Hapi = require('hapi');
const Rollover = require('rollover');
const { homedir } = require('os');
const { join } = require('path');
const Sso = require('hapi-triton-auth');
const Nav = require('my-joy-navigation');
const Api = require('cloudapi-gql');
const {
PORT = 3069,
COOKIE_PASSWORD,
COOKIE_DOMAIN,
SDC_KEY_PATH,
SDC_ACCOUNT,
SDC_KEY_ID,
SDC_URL,
BASE_URL = `http://0.0.0.0:${PORT}`,
ROLLBAR_SERVER_TOKEN,
NODE_ENV = 'development'
// CONSOLE = 'my-joy-instances'
} = process.env;
const Ui = require('my-joy-instances');
// const Instances = require('my-joy-instances');
// const Images = require('my-joy-images');
const server = Hapi.server({
debug: {
request: ['error']
},
port: PORT,
host: '127.0.0.1'
});
async function main() {
await server.register([
{
plugin: Rollover,
options: {
rollbar: {
accessToken: ROLLBAR_SERVER_TOKEN,
reportLevel: 'error'
}
}
},
{
plugin: Brule,
options: {
auth: false
}
},
{
plugin: Sso,
options: {
ssoUrl: 'https://sso.joyent.com',
baseUrl: BASE_URL,
apiBaseUrl: SDC_URL,
keyId: '/' + SDC_ACCOUNT + '/keys/' + SDC_KEY_ID,
keyPath: SDC_KEY_PATH || join(homedir(), '.ssh/id_rsa'),
permissions: { cloudapi: ['/my/*'] },
isDev: NODE_ENV === 'development',
cookie: {
isHttpOnly: true,
isSecure: false,
password: COOKIE_PASSWORD,
ttl: 1000 * 60 * 60, // 1 hour
domain: COOKIE_DOMAIN
}
}
},
{
plugin: Nav
},
{
plugin: Ui
},
{
plugin: Api,
options: {
keyId: '/' + SDC_ACCOUNT + '/keys/' + SDC_KEY_ID,
keyPath: SDC_KEY_PATH || join(homedir(), '.ssh/id_rsa'),
apiBaseUrl: SDC_URL
}
}
]);
server.auth.default('sso');
process.on('unhandledRejection', err => {
server.log(['error'], err);
});
await server.start();
console.log(`server started at http://localhost:${server.info.port}`);
}
main();

43
bundle/src/instances.js Normal file
View File

@ -0,0 +1,43 @@
const Main = require('apr-main');
const CloudApiGql = require('cloudapi-gql');
const Url = require('url');
const Server = require('./server');
const {
PORT = 4002,
BASE_URL = `http://0.0.0.0:${PORT}`,
PREFIX = 'instances',
DC_NAME,
SDC_URL,
SDC_KEY_PATH,
SDC_ACCOUNT,
SDC_KEY_ID
} = process.env;
const dcName = DC_NAME || Url.parse(SDC_URL).host.split('.')[0];
const keyPath = SDC_KEY_PATH;
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
const apiBaseUrl = SDC_URL;
Main(async () => {
const server = await Server({
PORT,
BASE_URL
});
await server.register({
plugin: CloudApiGql,
options: {
authStrategy: 'sso',
keyPath,
keyId,
apiBaseUrl,
dcName
},
routes: {
prefix: `/${PREFIX}`
}
});
await server.start();
});

49
bundle/src/navigation.js Normal file
View File

@ -0,0 +1,49 @@
const Main = require('apr-main');
const Nav = require('hapi-webconsole-nav');
const Url = require('url');
const Server = require('./server');
const Regions = require('../data/regions');
const Categories = require('../data/categories');
const {
PORT = 4001,
BASE_URL = `http://0.0.0.0:${PORT}`,
PREFIX = 'navigation',
DC_NAME,
SDC_URL,
SDC_KEY_PATH,
SDC_ACCOUNT,
SDC_KEY_ID
} = process.env;
const dcName = DC_NAME || Url.parse(SDC_URL).host.split('.')[0];
const keyPath = SDC_KEY_PATH;
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
const apiBaseUrl = SDC_URL;
const baseUrl = BASE_URL;
Main(async () => {
const server = await Server({
PORT,
BASE_URL
});
await server.register({
plugin: Nav,
options: {
keyPath,
keyId,
apiBaseUrl,
dcName,
baseUrl,
regions: Regions,
categories: Categories
},
routes: {
prefix: `/${PREFIX}`
}
});
await server.start();
});

83
bundle/src/server.js Normal file
View File

@ -0,0 +1,83 @@
const Hapi = require('hapi');
const Sso = require('hapi-triton-auth');
const Url = require('url');
const {
COOKIE_PASSWORD,
COOKIE_DOMAIN,
SDC_KEY_PATH,
SDC_ACCOUNT,
SDC_KEY_ID,
SDC_URL,
DC_NAME
} = process.env;
module.exports = async ({ PORT, BASE_URL }) => {
const dcName = DC_NAME || Url.parse(SDC_URL).host.split('.')[0];
const keyPath = SDC_KEY_PATH;
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
const apiBaseUrl = SDC_URL;
const ssoUrl = 'https://sso.joyent.com/login';
const baseUrl = BASE_URL;
const isDev = true;
const permissions = {
cloudapi: ['/my/*']
};
const cookie = {
password: COOKIE_PASSWORD,
domain: COOKIE_DOMAIN,
isSecure: false,
isHttpOnly: true,
ttl: 1000 * 60 * 60 // 1 hour
};
const server = Hapi.server({
port: PORT,
routes: {
cors: {
origin: ['*'],
credentials: true,
additionalHeaders: ['Cookie', 'X-CSRF-Token']
}
},
debug: {
log: ['error'],
request: ['error']
}
});
server.events.on('log', (event, tags) => {
if (tags.error) {
console.log(event);
}
});
server.events.on('request', (request, event) => {
const { tags } = event;
if (tags.includes('error') && event.data && event.data.errors) {
event.data.errors.forEach(error => {
console.log(error);
});
}
});
await server.register({
plugin: Sso,
options: {
keyPath,
keyId,
apiBaseUrl,
ssoUrl,
permissions,
baseUrl,
isDev,
cookie
}
});
server.auth.default('sso');
return server;
};

View File

@ -45,16 +45,31 @@
},
"resolutions": {
"axios": "0.16.2",
"core-js": "2.5.3",
"follow-redirects": "1.4.1",
"debug": "3.1.0",
"chalk": "2.1.0",
"minimist": "1.2.0",
"node-fetch": "2.1.1",
"ansi-styles": "3.2.1",
"strip-ansi": "4.0.0",
"supports-color": "4.5.0",
"ansi-regex": "3.0.0",
"core-js": "2.5.4",
"regenerator-runtime": "0.11.1",
"pify": "3.0.0",
"parse-json": "3.0.0",
"graphql": "0.13.0",
"hoist-non-react-statics": "2.5.0",
"stylis-rule-sheet": "0.0.10",
"react": "16.2.0",
"has-flag": "2.0.0",
"parse-json": "4.0.0",
"whatwg-fetch": "2.0.4",
"react": "16.3.1",
"breeze-nexttick": "0.2.1",
"isarray": "1.0.0"
"isarray": "1.0.0",
"boom": "7.2.0",
"lru-cache": "4.1.2",
"uuid": "3.0.1",
"extsprintf": "1.0.1"
},
"workspaces": ["packages/*", "bundle"]
"workspaces": [
"packages/*",
"bundle"
]
}

View File

@ -24,7 +24,7 @@
"dependencies": {
"remcalc": "^1.0.10",
"rnd-id": "^2.0.2",
"styled-components": "^3.2.1"
"styled-components": "^3.2.5"
},
"devDependencies": {
"babel-cli": "^6.26.0",
@ -32,10 +32,10 @@
"eslint": "^4.19.1",
"eslint-config-joyent-portal": "^3.3.1",
"joyent-react-scripts": "^8.0.3",
"react": "^16.2.0",
"redrun": "^6.0.1"
"react": "^16.3.1",
"redrun": "^6.0.2"
},
"peerDependencies": {
"react": "^16.2.0"
"react": "^16.3.1"
}
}

View File

@ -28,16 +28,15 @@
"babel-preset-joyent-portal": "^7.0.1",
"eslint": "^4.19.1",
"eslint-config-joyent-portal": "^3.3.1",
"execa": "^0.9.0",
"execa": "^0.10.0",
"globby": "^8.0.1",
"htmltojsx": "^0.3.0",
"joyent-react-scripts": "^8.0.3",
"mz": "^2.7.0",
"prettier": "^1.11.0",
"react": "^16.2.0",
"redrun": "^6.0.1"
"react": "^16.3.1",
"redrun": "^6.0.2"
},
"peerDependencies": {
"react": "^16.2.0"
"react": "^16.3.1"
}
}

View File

@ -32,7 +32,7 @@
"exenv": "^1.2.2",
"force-array": "^3.1.0",
"fuse.js": "^3.2.0",
"hapi-render-react": "^2.2.0",
"hapi-render-react": "^2.5.2",
"hapi-render-react-joyent-document": "^5.0.0",
"inert": "^5.1.0",
"joyent-logo-assets": "^1.1.0",
@ -49,19 +49,19 @@
"lunr": "^2.1.6",
"mz": "^2.7.0",
"param-case": "^2.1.1",
"react": "^16.2.0",
"react-apollo": "^2.1.1",
"react-dom": "^16.2.0",
"react": "^16.3.1",
"react-apollo": "^2.1.2",
"react-dom": "^16.3.1",
"react-redux": "^5.0.7",
"react-redux-values": "^1.1.2",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"redux": "^3.7.2",
"redux-form": "^7.2.3",
"redux-form": "^7.3.0",
"remcalc": "^1.0.10",
"styled-components": "^3.2.1",
"styled-components": "^3.2.5",
"styled-components-spacing": "^2.1.3",
"styled-flex-component": "^2.2.1",
"styled-flex-component": "^2.2.2",
"styled-is": "^1.1.2",
"title-case": "^2.1.1",
"yup": "^0.24.1"
@ -71,11 +71,11 @@
"babel-preset-joyent-portal": "^7.0.1",
"eslint": "^4.19.1",
"eslint-config-joyent-portal": "^3.3.1",
"jest-image-snapshot": "^2.3.0",
"jest-styled-components": "^5.0.0",
"jest-image-snapshot": "^2.4.0",
"jest-styled-components": "^5.0.1",
"joyent-react-scripts": "^8.0.3",
"react-screenshot-renderer": "^1.1.2",
"react-test-renderer": "^16.2.0",
"redrun": "^6.0.1"
"react-test-renderer": "^16.3.1",
"redrun": "^6.0.2"
}
}

View File

@ -23,14 +23,14 @@
"apr-intercept": "^3.0.3",
"apr-reduce": "^3.0.3",
"bytes": "^3.0.0",
"clipboard-copy": "^1.4.2",
"clipboard-copy": "^2.0.0",
"constant-case": "^2.0.0",
"cross-fetch": "^1.1.1",
"cross-fetch": "^2.1.0",
"date-fns": "^1.29.0",
"declarative-redux-form": "^2.0.8",
"exenv": "^1.2.2",
"fuse.js": "^3.2.0",
"hapi-render-react": "^2.2.0",
"hapi-render-react": "^2.5.2",
"hapi-render-react-joyent-document": "^5.0.0",
"inert": "^5.1.0",
"joyent-logo-assets": "^1.1.0",
@ -56,22 +56,21 @@
"lodash.sortby": "^4.7.0",
"lodash.uniqby": "^4.7.0",
"lodash.values": "^4.3.0",
"my-joy-images": "1.1.5",
"param-case": "^2.1.1",
"query-string": "^5.1.0",
"react": "^16.2.0",
"react-apollo": "^2.1.1",
"react-dom": "^16.2.0",
"query-string": "^6.0.0",
"react": "^16.3.1",
"react-apollo": "^2.1.2",
"react-dom": "^16.3.1",
"react-redux": "^5.0.7",
"react-redux-values": "^1.1.2",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"redux": "^3.7.2",
"redux-form": "^7.2.3",
"redux-form": "^7.3.0",
"remcalc": "^1.0.10",
"styled-components": "^3.2.1",
"styled-components": "^3.2.5",
"styled-components-spacing": "^2.1.3",
"styled-flex-component": "^2.2.1",
"styled-flex-component": "^2.2.2",
"title-case": "^2.1.1",
"yup": "^0.24.1"
},
@ -80,11 +79,11 @@
"babel-preset-joyent-portal": "^7.0.1",
"eslint": "^4.19.1",
"eslint-config-joyent-portal": "^3.3.1",
"jest-image-snapshot": "^2.3.0",
"jest-styled-components": "^5.0.0",
"jest-image-snapshot": "^2.4.0",
"jest-styled-components": "^5.0.1",
"joyent-react-scripts": "^8.0.3",
"react-screenshot-renderer": "^1.1.2",
"react-test-renderer": "^16.2.0",
"redrun": "^6.0.1"
"react-test-renderer": "^16.3.1",
"redrun": "^6.0.2"
}
}

View File

@ -5,7 +5,7 @@
"license": "MPL-2.0",
"main": "lib/index.js",
"scripts": {
"dev": "NAMESPACE=navigation NODE_ENV=development PREACT=1 REACT_APP_GQL_PORT=3068 PORT=3000 joyent-react-scripts start",
"dev": "NAMESPACE=navigation NODE_ENV=development PREACT=1 REACT_APP_GQL_PORT=4000 PORT=3000 joyent-react-scripts start",
"build:test": "echo 0",
"build:lib": "echo 0",
"build:bundle": "NAMESPACE=navigation NODE_ENV=production redrun build",
@ -23,7 +23,7 @@
"apollo-link-http": "^1.5.3",
"apollo-link-state": "^0.4.1",
"apr-intercept": "^3.0.3",
"emotion": "^9.1.0",
"emotion": "^9.1.1",
"emotion-theming": "^9.0.0",
"graphql-tag": "^2.8.0",
"inert": "^5.1.0",
@ -37,9 +37,9 @@
"pascal-case": "^2.0.1",
"preact": "^8.2.7",
"preact-compat": "^3.18.0",
"preact-emotion": "^9.1.0",
"preact-emotion": "^9.1.1",
"preact-emotion-flexboxgrid": "^2.0.1",
"react-apollo": "^2.1.1",
"react-apollo": "^2.1.2",
"remcalc": "^1.0.10",
"stickybits": "^3.2.0"
},
@ -49,6 +49,6 @@
"eslint": "^4.19.1",
"eslint-config-joyent-portal": "^3.3.1",
"joyent-react-scripts": "^8.0.3",
"redrun": "^6.0.1"
"redrun": "^6.0.2"
}
}

View File

@ -25,7 +25,7 @@
},
"dependencies": {
"camel-case": "^3.0.0",
"clipboard-copy": "^1.4.2",
"clipboard-copy": "^2.0.0",
"exenv": "^1.2.2",
"joy-react-broadcast": "^0.6.9",
"joyent-icons": "^5.1.0",
@ -40,14 +40,14 @@
"pascal-case": "^2.0.1",
"prop-types": "^15.6.1",
"react-bundle": "^1.1.0",
"react-popper": "^0.8.2",
"react-responsive": "^4.0.4",
"react-popper": "^0.9.5",
"react-responsive": "^4.1.0",
"react-scroll-parallax": "^1.3.3",
"remcalc": "^1.0.10",
"rnd-id": "^2.0.2",
"styled-components": "^3.2.1",
"styled-components": "^3.2.5",
"styled-components-spacing": "^2.1.3",
"styled-flex-component": "^2.2.1",
"styled-flex-component": "^2.2.2",
"styled-is": "^1.1.2",
"unitcalc": "^1.2.3"
},
@ -57,19 +57,19 @@
"classnames": "^2.2.5",
"eslint": "^4.19.1",
"eslint-config-joyent-portal": "^3.3.1",
"jest-styled-components": "^5.0.0",
"jest-styled-components": "^5.0.1",
"joyent-react-scripts": "^8.0.3",
"react": "^16.2.0",
"react": "^16.3.1",
"react-docgen": "^3.0.0-beta8",
"react-docgen-displayname-handler": "^1.0.1",
"react-dom": "^16.2.0",
"react-styleguidist": "^6.2.5",
"react-test-renderer": "^16.2.0",
"redrun": "^6.0.1"
"react-dom": "^16.3.1",
"react-styleguidist": "^7.0.4",
"react-test-renderer": "^16.3.1",
"redrun": "^6.0.2"
},
"peerDependencies": {
"joyent-manifest-editor": "^1.4.0",
"react": "^16.2.0",
"react-dom": "^16.2.0"
"react": "^16.3.1",
"react-dom": "^16.3.1"
}
}

View File

@ -8,7 +8,6 @@
"scripts": {
"dev": "NODE_ENV=development joyent-react-scripts start",
"build": "NODE_ENV=production joyent-react-scripts build",
"fmt": "prettier --write --single-quote *.js src/*.js src/**/*.js",
"lint-ci": "eslint . --fix --ext .js --ext .md && echo 0 `# stylelint './src/**/*.js'`",
"lint": "eslint . --fix --ext .js --ext .md && echo 0 `# stylelint './src/**/*.js'`",
"prepublish": "echo 0"
@ -33,7 +32,6 @@
"eslint": "^4.11.0",
"eslint-config-joyent-portal": "^3.2.0",
"joyent-react-scripts": "^8.0.2",
"prettier": "^1.8.2",
"stylelint": "^8.4.0",
"stylelint-config-joyent-portal": "^2.0.1"
}

511
yarn.lock

File diff suppressed because it is too large Load Diff