chore: update from develop

This commit is contained in:
Gervwyk 2020-10-30 16:48:19 +02:00
commit efbb840a59
135 changed files with 2776 additions and 1188 deletions

View File

@ -17,6 +17,7 @@ jobs:
node-version: '12.x'
- uses: actions/checkout@v2
with:
# needed for yarn version check, checks out entire repo
fetch-depth: 0
- name: Check yarn cache integrity
run: yarn install --immutable --immutable-cache --check-cache

1590
.pnp.js generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -4,7 +4,6 @@ releases:
"@lowdefy/cli": patch
"@lowdefy/color": patch
"@lowdefy/engine": patch
"@lowdefy/express": patch
"@lowdefy/format": patch
"@lowdefy/graphql": patch
"@lowdefy/helpers": patch
@ -14,3 +13,4 @@ releases:
"@lowdefy/nunjucks": patch
"@lowdefy/operators": patch
"@lowdefy/renderer": patch
"@lowdefy/server": patch

View File

@ -30,7 +30,8 @@
"clean": "lerna run clean",
"prepare": "lerna run prepare",
"prettier": "prettier --config .prettierrc --write **/*.js",
"test": "lerna run test"
"test": "lerna run test",
"test:ci": "yarn install --immutable --immutable-cache --check-cache && yarn version check && yarn build && yarn test --ignore='@lowdefy/format'"
},
"devDependencies": {
"@yarnpkg/pnpify": "2.3.3",

View File

@ -57,6 +57,7 @@
"babel-loader": "8.1.0",
"babel-plugin-import": "1.13.1",
"buffer": "5.7.0",
"clean-webpack-plugin": "3.0.0",
"css-loader": "5.0.0",
"enzyme": "3.11.0",
"html-webpack-plugin": "4.5.0",

View File

@ -1,4 +1,5 @@
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const path = require('path');
module.exports = {
@ -55,6 +56,7 @@ module.exports = {
],
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html',
}),

View File

@ -1,6 +1,6 @@
{
"name": "@lowdefy/build",
"version": "0.0.0-experimental.0",
"version": "0.0.0-alpha.2",
"licence": "Apache-2.0",
"description": "",
"homepage": "https://lowdefy.com",
@ -55,6 +55,7 @@
"@babel/preset-env": "7.12.1",
"babel-jest": "26.6.1",
"babel-loader": "8.1.0",
"clean-webpack-plugin": "3.0.0",
"jest": "26.6.1",
"webpack": "5.3.2",
"webpack-cli": "4.1.0"

View File

@ -15,11 +15,17 @@
*/
const path = require('path');
const build = require('./dist/index.js').default;
build({
logger: console,
cacheDirectory: path.resolve(process.cwd(), '.lowdefy/.cache'),
configDirectory: process.cwd(),
outputDirectory: path.resolve(process.cwd(), '.lowdefy/build'),
});
async function run() {
// Doing weird things for webpack module federation.
// Webpack needs an async import to resolve shared dependencies.
const build = await require('./dist/index.js').default.then((module) => module.default);
await build({
logger: console,
cacheDirectory: path.resolve(process.cwd(), '.lowdefy/.cache'),
configDirectory: process.cwd(),
outputDirectory: path.resolve(process.cwd(), '.lowdefy/build'),
});
}
run();

View File

@ -0,0 +1,70 @@
/* eslint-disable no-console */
/*
Copyright 2020 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import createFileLoader from './loaders/fileLoader';
import createFileSetter from './loaders/fileSetter';
import createMetaLoader from './loaders/metaLoader';
import buildConnections from './build/buildConnections';
import buildMenu from './build/buildMenu';
import buildPages from './build/buildPages';
import buildRefs from './build/buildRefs';
import cleanOutputDirectory from './build/cleanOutputDirectory';
import testSchema from './build/testSchema';
import writeConnections from './build/writeConnections';
import writeGlobal from './build/writeGlobal';
import writeMenus from './build/writeMenus';
import writePages from './build/writePages';
import writeRequests from './build/writeRequests';
function createContext(options) {
const { logger, cacheDirectory, configDirectory, outputDirectory } = options;
const context = {
logger,
configLoader: createFileLoader({ baseDirectory: configDirectory }),
artifactSetter: createFileSetter({ baseDirectory: outputDirectory }),
outputDirectory,
cacheDirectory,
};
return context;
}
async function build(options) {
const context = createContext(options);
try {
let components = await buildRefs({ context });
await testSchema({ components, context });
context.metaLoader = createMetaLoader({ components, context });
await buildConnections({ components, context });
await buildPages({ components, context });
await buildMenu({ components, context });
await cleanOutputDirectory({ context });
await writeConnections({ components, context });
await writeRequests({ components, context });
await writePages({ components, context });
await writeGlobal({ components, context });
await writeMenus({ components, context });
} catch (error) {
context.logger.error(error);
throw error;
}
}
export { createContext };
export default build;

View File

@ -26,7 +26,6 @@ async function writeConnections({ components, context }) {
filePath: `connections/${connection.connectionId}.json`,
content: JSON.stringify(connection, null, 2),
});
await context.logger.info(`Updated connection ${connection.connectionId}`);
});
return Promise.all(writePromises);
}

View File

@ -17,21 +17,15 @@
import writeConnections from './writeConnections';
import testContext from '../test/testContext';
const mockLogInfo = jest.fn();
const mockSet = jest.fn();
const logger = {
info: mockLogInfo,
};
const artifactSetter = {
set: mockSet,
};
const context = testContext({ logger, artifactSetter });
const context = testContext({ artifactSetter });
beforeEach(() => {
mockLogInfo.mockReset();
mockSet.mockReset();
});
@ -62,7 +56,6 @@ test('writeConnections write connection', async () => {
},
],
]);
expect(mockLogInfo.mock.calls).toEqual([['Updated connection connection1']]);
});
test('writeConnections multiple connection', async () => {
@ -99,10 +92,6 @@ test('writeConnections multiple connection', async () => {
},
],
]);
expect(mockLogInfo.mock.calls).toEqual([
['Updated connection connection1'],
['Updated connection connection2'],
]);
});
test('writeConnections no connections', async () => {
@ -111,14 +100,12 @@ test('writeConnections no connections', async () => {
};
await writeConnections({ components, context });
expect(mockSet.mock.calls).toEqual([]);
expect(mockLogInfo.mock.calls).toEqual([]);
});
test('writeConnections connections undefined', async () => {
const components = {};
await writeConnections({ components, context });
expect(mockSet.mock.calls).toEqual([]);
expect(mockLogInfo.mock.calls).toEqual([]);
});
test('writeConnections connections not an array', async () => {

View File

@ -27,7 +27,6 @@ async function writeGlobal({ components, context }) {
filePath: 'global.json',
content: JSON.stringify(components.global, null, 2),
});
await context.logger.info('Updated global');
}
export default writeGlobal;

View File

@ -17,21 +17,15 @@
import writeGlobal from './writeGlobal';
import testContext from '../test/testContext';
const mockLogInfo = jest.fn();
const mockSet = jest.fn();
const logger = {
info: mockLogInfo,
};
const artifactSetter = {
set: mockSet,
};
const context = testContext({ logger, artifactSetter });
const context = testContext({ artifactSetter });
beforeEach(() => {
mockLogInfo.mockReset();
mockSet.mockReset();
});
@ -52,7 +46,6 @@ test('writeGlobal', async () => {
},
],
]);
expect(mockLogInfo.mock.calls).toEqual([['Updated global']]);
});
test('writeGlobal empty global', async () => {
@ -68,7 +61,6 @@ test('writeGlobal empty global', async () => {
},
],
]);
expect(mockLogInfo.mock.calls).toEqual([['Updated global']]);
});
test('writeGlobal global undefined', async () => {
@ -82,7 +74,6 @@ test('writeGlobal global undefined', async () => {
},
],
]);
expect(mockLogInfo.mock.calls).toEqual([['Updated global']]);
});
test('writeGlobal global not an object', async () => {

View File

@ -24,7 +24,6 @@ async function writeMenus({ components, context }) {
filePath: 'menus.json',
content: JSON.stringify(components.menus, null, 2),
});
await context.logger.info('Updated menus');
}
export default writeMenus;

View File

@ -17,21 +17,15 @@
import writeMenus from './writeMenus';
import testContext from '../test/testContext';
const mockLogInfo = jest.fn();
const mockSet = jest.fn();
const logger = {
info: mockLogInfo,
};
const artifactSetter = {
set: mockSet,
};
const context = testContext({ logger, artifactSetter });
const context = testContext({ artifactSetter });
beforeEach(() => {
mockLogInfo.mockReset();
mockSet.mockReset();
});
@ -60,7 +54,6 @@ test('writeMenus', async () => {
},
],
]);
expect(mockLogInfo.mock.calls).toEqual([['Updated menus']]);
});
test('writeMenus empty menus', async () => {
@ -76,7 +69,6 @@ test('writeMenus empty menus', async () => {
},
],
]);
expect(mockLogInfo.mock.calls).toEqual([['Updated menus']]);
});
test('writeMenus menus undefined', async () => {

View File

@ -24,7 +24,6 @@ async function writePage({ page, context }) {
filePath: `pages/${page.pageId}/${page.pageId}.json`,
content: JSON.stringify(page, null, 2),
});
await context.logger.info(`Updated page ${page.pageId}`);
}
async function writePages({ components, context }) {

View File

@ -17,21 +17,15 @@
import writePages from './writePages';
import testContext from '../test/testContext';
const mockLogInfo = jest.fn();
const mockSet = jest.fn();
const logger = {
info: mockLogInfo,
};
const artifactSetter = {
set: mockSet,
};
const context = testContext({ logger, artifactSetter });
const context = testContext({ artifactSetter });
beforeEach(() => {
mockLogInfo.mockReset();
mockSet.mockReset();
});
@ -62,7 +56,6 @@ test('writePages write page', async () => {
},
],
]);
expect(mockLogInfo.mock.calls).toEqual([['Updated page page1']]);
});
test('writePages multiple pages', async () => {
@ -111,7 +104,6 @@ test('writePages multiple pages', async () => {
},
],
]);
expect(mockLogInfo.mock.calls).toEqual([['Updated page page1'], ['Updated page page2']]);
});
test('writePages no pages', async () => {
@ -120,14 +112,12 @@ test('writePages no pages', async () => {
};
await writePages({ components, context });
expect(mockSet.mock.calls).toEqual([]);
expect(mockLogInfo.mock.calls).toEqual([]);
});
test('writePages pages undefined', async () => {
const components = {};
await writePages({ components, context });
expect(mockSet.mock.calls).toEqual([]);
expect(mockLogInfo.mock.calls).toEqual([]);
});
test('writePages pages not an array', async () => {

View File

@ -65,14 +65,15 @@ async function updateRequestsOnPage({ page, context }) {
filePath: `pages/${page.pageId}/requests/${request.requestId}.json`,
content: JSON.stringify(request, null, 2),
});
await context.logger.info(`Updated request ${request.requestId} on page ${page.pageId}`);
});
const writeMutationPromises = mutations.map(async (mutation) => {
await context.artifactSetter.set({
filePath: `pages/${page.pageId}/mutations/${mutation.mutationId}.json`,
content: JSON.stringify(mutation, null, 2),
});
await context.logger.info(`Updated mutation ${mutation.mutationId} on page ${page.pageId}`);
await context.logger.info(
`Updated mutation "${mutation.mutationId}" on page "${page.pageId}".`
);
});
return Promise.all([...writeRequestPromises, ...writeMutationPromises]);

View File

@ -17,21 +17,15 @@
import writeRequests from './writeRequests';
import testContext from '../test/testContext';
const mockLogInfo = jest.fn();
const mockSet = jest.fn();
const logger = {
info: mockLogInfo,
};
const artifactSetter = {
set: mockSet,
};
const context = testContext({ logger, artifactSetter });
const context = testContext({ artifactSetter });
beforeEach(() => {
mockLogInfo.mockReset();
mockSet.mockReset();
});
@ -69,7 +63,6 @@ test('writeRequests write request', async () => {
},
],
]);
expect(mockLogInfo.mock.calls).toEqual([['Updated request request1 on page page1']]);
});
test('writeRequests write nested request', async () => {
@ -116,7 +109,6 @@ test('writeRequests write nested request', async () => {
},
],
]);
expect(mockLogInfo.mock.calls).toEqual([['Updated request request1 on page page1']]);
});
test('writeRequests add mutation', async () => {
@ -153,7 +145,6 @@ test('writeRequests add mutation', async () => {
},
],
]);
expect(mockLogInfo.mock.calls).toEqual([['Updated mutation mutation1 on page page1']]);
});
test('writeRequests add nested mutation', async () => {
@ -199,7 +190,6 @@ test('writeRequests add nested mutation', async () => {
},
],
]);
expect(mockLogInfo.mock.calls).toEqual([['Updated mutation mutation1 on page page1']]);
});
test('writeRequests requests is not an array', async () => {
@ -238,14 +228,12 @@ test('writeRequests empty pages array', async () => {
};
await writeRequests({ components, context });
expect(mockSet.mock.calls).toEqual([]);
expect(mockLogInfo.mock.calls).toEqual([]);
});
test('writeRequests no pages array', async () => {
const components = {};
await writeRequests({ components, context });
expect(mockSet.mock.calls).toEqual([]);
expect(mockLogInfo.mock.calls).toEqual([]);
});
test('writeRequests pages not an array', async () => {

View File

@ -1,5 +1,3 @@
/* eslint-disable no-console */
/*
Copyright 2020 Lowdefy, Inc
@ -16,57 +14,6 @@
limitations under the License.
*/
import { type } from '@lowdefy/helpers';
import createFileLoader from './loaders/fileLoader';
import createFileSetter from './loaders/fileSetter';
import createMetaLoader from './loaders/metaLoader';
import buildConnections from './build/buildConnections';
import buildMenu from './build/buildMenu';
import buildPages from './build/buildPages';
import buildRefs from './build/buildRefs';
import cleanOutputDirectory from './build/cleanOutputDirectory';
import testSchema from './build/testSchema';
import writeConnections from './build/writeConnections';
import writeGlobal from './build/writeGlobal';
import writeMenus from './build/writeMenus';
import writePages from './build/writePages';
import writeRequests from './build/writeRequests';
function createContext(options) {
const { logger, cacheDirectory, configDirectory, outputDirectory } = options;
const context = {
logger,
configLoader: createFileLoader({ baseDirectory: configDirectory }),
artifactSetter: createFileSetter({ baseDirectory: outputDirectory }),
outputDirectory,
cacheDirectory,
};
return context;
}
async function build(options) {
const context = createContext(options);
try {
let components = await buildRefs({ context });
await testSchema({ components, context });
context.metaLoader = createMetaLoader({ components, context });
await buildConnections({ components, context });
await buildPages({ components, context });
await buildMenu({ components, context });
await cleanOutputDirectory({ context });
await writeConnections({ components, context });
await writeRequests({ components, context });
await writePages({ components, context });
await writeGlobal({ components, context });
await writeMenus({ components, context });
} catch (error) {
context.logger.error(error);
throw error;
}
}
export { createContext };
const build = import('./build');
export default build;

View File

@ -1,5 +1,6 @@
const path = require('path');
const { ModuleFederationPlugin } = require('webpack').container;
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { dependencies } = require('./package.json');
module.exports = {
@ -37,12 +38,13 @@ module.exports = {
],
},
plugins: [
new CleanWebpackPlugin(),
new ModuleFederationPlugin({
name: 'build',
library: { type: 'commonjs' },
filename: 'remoteEntry.js',
exposes: {
'./build': './src/index.js',
'./build': './src/build.js',
},
shared: dependencies,
}),

View File

@ -7,6 +7,7 @@
"node": "12"
}
}
]
],
"@babel/preset-react"
]
}

View File

@ -3,7 +3,13 @@ module.exports = {
collectCoverage: true,
collectCoverageFrom: ['src/**/*.js'],
coverageDirectory: 'coverage',
coveragePathIgnorePatterns: ['<rootDir>/dist/', '<rootDir>/src/test'],
coveragePathIgnorePatterns: [
'<rootDir>/dist/',
'<rootDir>/src/test',
'<rootDir>/src/index.js',
'<rootDir>/src/index.js',
'<rootDir>/src/commands/dev/shell/*',
],
coverageReporters: [['lcov', { projectRoot: '../..' }], 'text', 'clover'],
errorOnDeprecated: true,
testEnvironment: 'node',

View File

@ -1,22 +0,0 @@
version: '1.0.1-experimental.1'
types:
Context:
url: http://localhost:3002/meta/Context.json
Button:
url: http://localhost:3002/meta/Button.json
connections:
- id: con1
type: AxiosHttp
pages:
- id: page1
type: Context
requests:
- id: req1
type: AxiosHttp
connectionId: con1
blocks:
- id: button
type: Button

View File

@ -1,6 +1,6 @@
{
"name": "@lowdefy/cli",
"version": "3.0.0-experimental.1",
"version": "3.0.0-alpha.3",
"license": "Apache-2.0",
"description": "Lowdefy CLI",
"homepage": "https://lowdefy.com",
@ -36,7 +36,7 @@
"npm-publish": "npm publish --access public",
"prepare": "yarn build",
"prepublishOnly": "yarn build",
"test": "jest --coverage --passWithNoTests",
"test": "FORCE_COLOR=3 jest --coverage",
"webpack": "webpack --config webpack.config.js",
"version:prerelease": "yarn version prerelease",
"version:patch": "yarn version patch -d",
@ -44,25 +44,37 @@
"version:major": "yarn version major -d"
},
"dependencies": {
"@lowdefy/build": "0.0.0-experimental.0",
"@lowdefy/block-tools": "1.0.1-alpha.9",
"@lowdefy/helpers": "1.1.0",
"@lowdefy/node-utils": "1.0.0",
"apollo-server-express": "2.18.2",
"axios": "0.21.0",
"chalk": "4.1.0",
"chokidar": "3.4.3",
"commander": "6.2.0",
"decompress": "4.2.1",
"decompress-targz": "4.1.1",
"express": "4.17.1",
"graphql": "15.4.0",
"inquirer": "7.3.3",
"js-yaml": "3.14.0"
"js-yaml": "3.14.0",
"opener": "1.5.2",
"react": "17.0.1",
"react-dom": "17.0.1",
"reload": "3.1.1"
},
"devDependencies": {
"@babel/cli": "7.12.1",
"@babel/core": "7.12.3",
"@babel/preset-env": "7.12.1",
"@babel/preset-react": "7.12.1",
"babel-jest": "26.5.2",
"babel-loader": "8.1.0",
"clean-webpack-plugin": "3.0.0",
"css-loader": "5.0.0",
"html-webpack-plugin": "4.5.0",
"jest": "26.5.3",
"style-loader": "2.0.0",
"webpack": "5.3.2",
"webpack-cli": "4.0.0"
}

View File

@ -16,25 +16,21 @@
import path from 'path';
import getBuildScript from './getBuildScript';
import getLowdefyVersion from '../../utils/getLowdefyVersion';
import createPrint from '../../utils/print';
import { cacheDirectoryPath, outputDirectoryPath } from '../../utils/directories';
import createContext from '../../utils/context';
import { outputDirectoryPath } from '../../utils/directories';
async function build(program) {
let baseDirectory = process.cwd();
if (program.baseDirectory) {
baseDirectory = path.resolve(program.baseDirectory);
}
const version = await getLowdefyVersion(program.baseDirectory);
const cacheDirectory = path.resolve(baseDirectory, cacheDirectoryPath);
const buildScript = await getBuildScript(version, cacheDirectory);
buildScript({
logger: createPrint({ timestamp: true }),
cacheDirectory,
configDirectory: baseDirectory,
outputDirectory: path.resolve(baseDirectory, outputDirectoryPath),
async function build(options) {
const context = await createContext(options);
await getBuildScript(context);
const outputDirectory = path.resolve(context.baseDirectory, outputDirectoryPath);
context.print.info('Starting build.');
await context.buildScript({
logger: context.print,
cacheDirectory: context.cacheDirectory,
configDirectory: context.baseDirectory,
outputDirectory,
});
context.print.info(`Build artifacts saved at ${outputDirectory}.`);
}
export default build;

View File

@ -0,0 +1,80 @@
/*
Copyright 2020 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import path from 'path';
import build from './build';
import getBuildScript from './getBuildScript';
import createContext from '../../utils/context';
const info = jest.fn();
jest.mock('./getBuildScript', () => {
const buildScript = jest.fn();
return (context) => {
context.buildScript = buildScript;
return context;
};
});
jest.mock('../../utils/context', () => {
const createContext = jest.fn();
return createContext;
});
beforeEach(() => {
createContext.mockReset();
});
test('build', async () => {
const baseDirectory = process.cwd();
const cacheDirectory = path.resolve(process.cwd(), '.lowdefy/.cache');
const outputDirectory = path.resolve(process.cwd(), '.lowdefy/build');
createContext.mockImplementation(() => ({
print: {
info,
},
baseDirectory,
cacheDirectory,
}));
await build({});
const context = createContext.mock.results[0].value;
const { buildScript } = context;
expect(createContext).toHaveBeenCalledTimes(1);
expect(buildScript).toHaveBeenCalledTimes(1);
expect(buildScript.mock.calls[0][0].outputDirectory).toEqual(outputDirectory);
expect(buildScript.mock.calls[0][0].cacheDirectory).toEqual(cacheDirectory);
expect(buildScript.mock.calls[0][0].outputDirectory).toEqual(outputDirectory);
});
test('build with base directory', async () => {
const baseDirectory = path.resolve(process.cwd(), 'baseDirectory');
const cacheDirectory = path.resolve(process.cwd(), 'baseDirectory/.lowdefy/.cache');
const outputDirectory = path.resolve(process.cwd(), 'baseDirectory/.lowdefy/build');
createContext.mockImplementation(() => ({
print: {
info,
},
baseDirectory,
cacheDirectory,
}));
await build({ baseDirectory: 'baseDirectory' });
const context = createContext.mock.results[0].value;
const { buildScript } = context;
expect(createContext).toHaveBeenCalledTimes(1);
expect(buildScript).toHaveBeenCalledTimes(1);
expect(buildScript.mock.calls[0][0].outputDirectory).toEqual(outputDirectory);
expect(buildScript.mock.calls[0][0].cacheDirectory).toEqual(cacheDirectory);
expect(buildScript.mock.calls[0][0].outputDirectory).toEqual(outputDirectory);
});

View File

@ -19,15 +19,15 @@ import path from 'path';
import loadBuildScriptToCache from './loadBuildScriptToCache';
import loadModule from '../../utils/loadModule';
async function getBuildScript(version, cacheDirectory) {
let buildScript;
const cleanVersion = version.replace(/[-.]/g, '_');
const cachePath = path.resolve(cacheDirectory, `scripts/build_${cleanVersion}`);
async function getBuildScript(context) {
const cleanVersion = context.version.replace(/[-.]/g, '_');
const cachePath = path.resolve(context.cacheDirectory, `scripts/build_${cleanVersion}`);
if (!fs.existsSync(path.resolve(cachePath, 'package/dist/remoteEntry.js'))) {
await loadBuildScriptToCache(version, cachePath);
await loadBuildScriptToCache(context, cachePath);
}
buildScript = await loadModule(cachePath, './build');
return buildScript.default;
const buildScript = await loadModule(path.resolve(cachePath, 'package/dist'), './build');
context.buildScript = buildScript.default;
return context;
}
export default getBuildScript;

View File

@ -18,7 +18,7 @@ import axios from 'axios';
import decompress from 'decompress';
import decompressTargz from 'decompress-targz';
async function loadBuildScriptToCache(version, cachePath) {
async function loadBuildScriptToCache(context, cachePath) {
const registryUrl = 'https://registry.npmjs.org/@lowdefy/build';
const packageInfo = await axios.get(registryUrl);
if (!packageInfo || !packageInfo.data) {
@ -27,16 +27,18 @@ async function loadBuildScriptToCache(version, cachePath) {
`Build package could not be found at ${registryUrl}. Check internet connection.`
);
}
if (!packageInfo.data.versions[version]) {
throw new Error(`Invalid Lowdefy version. Version "${version}" does not exist.`);
if (!packageInfo.data.versions[context.version]) {
throw new Error(`Invalid Lowdefy version. Version "${context.version}" does not exist.`);
}
const tarball = await axios.get(packageInfo.data.versions[version].dist.tarball, {
const tarball = await axios.get(packageInfo.data.versions[context.version].dist.tarball, {
responseType: 'arraybuffer',
});
if (!tarball || !tarball.data) {
/// TODO: Check if user has internet connection.
throw new Error(
`Build script could not be fetched from ${packageInfo.data.versions[version].dist.tarball}. Check internet connection.`
`Build script could not be fetched from ${
packageInfo.data.versions[context.version].dist.tarball
}. Check internet connection.`
);
}
await decompress(tarball.data, cachePath, {

View File

@ -0,0 +1,57 @@
/*
Copyright 2020 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import path from 'path';
import { cleanDirectory } from '@lowdefy/node-utils';
import cleanCache from './cleanCache';
import createPrint from '../../utils/print';
jest.mock('@lowdefy/node-utils', () => {
const cleanDirectory = jest.fn();
return { cleanDirectory };
});
jest.mock('../../utils/print', () => {
const info = jest.fn();
return () => ({
info,
});
});
const print = createPrint();
beforeEach(() => {
cleanDirectory.mockReset();
});
test('cleanCache', async () => {
await cleanCache({});
const cachePath = path.resolve(process.cwd(), './.lowdefy/.cache');
expect(cleanDirectory.mock.calls).toEqual([[cachePath]]);
expect(print.info.mock.calls).toEqual([
[`Cleaning cache at "${cachePath}".`],
['Cache cleaned.'],
]);
});
test('cleanCache baseDir', async () => {
await cleanCache({ baseDirectory: 'baseDir' });
const cachePath = path.resolve(process.cwd(), 'baseDir/.lowdefy/.cache');
expect(cleanDirectory.mock.calls).toEqual([[cachePath]]);
expect(print.info.mock.calls).toEqual([
[`Cleaning cache at "${cachePath}".`],
['Cache cleaned.'],
]);
});

View File

@ -0,0 +1,96 @@
/*
Copyright 2020 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import path from 'path';
import chokidar from 'chokidar';
import express from 'express';
import reload from 'reload';
import opener from 'opener';
import { ApolloServer } from 'apollo-server-express';
import BatchChanges from '../../utils/BatchChanges';
import createContext from '../../utils/context';
import getBuildScript from '../build/getBuildScript';
import getGraphql from './getGraphql';
import { outputDirectoryPath } from '../../utils/directories';
async function dev(options) {
// Setup
if (!options.port) options.port = 3000;
const context = await createContext(options);
await getBuildScript(context);
await getGraphql(context);
context.print.info('Starting development server.');
//Graphql
const config = {
DEPLOYMENT_ID: 'DEPLOYMENT_ID',
DEPLOYMENT_NAME: 'DEPLOYMENT_NAME',
DOMAIN_NAME: 'DOMAIN_NAME',
CONFIGURATION_BASE_PATH: path.resolve(process.cwd(), './.lowdefy/build'),
logger: console,
getHeadersFromInput: ({ req }) => req.headers,
getSecrets: () => ({
CONNECTION_SECRETS: {},
}),
};
const { typeDefs, resolvers, createContext: createGqlContext } = context.graphql;
const gqlContext = createGqlContext(config);
const server = new ApolloServer({ typeDefs, resolvers, context: gqlContext });
// Express
const app = express();
app.set('port', options.port);
server.applyMiddleware({ app, path: '/api/graphql' });
const reloadReturned = await reload(app, { route: '/api/dev/reload.js' });
app.use(express.static(path.join(__dirname, 'shell')));
app.use('/api/dev/version', (req, res) => {
res.json(context.version);
});
app.use((req, res) => {
res.sendFile(path.resolve(__dirname, 'shell/index.html'));
});
// File watcher
const fn = async () => {
context.print.info('Building configuration.');
await context.buildScript({
logger: context.print,
cacheDirectory: context.cacheDirectory,
configDirectory: context.baseDirectory,
outputDirectory: path.resolve(context.baseDirectory, outputDirectoryPath),
});
reloadReturned.reload();
};
const batchChanges = new BatchChanges({ fn, context });
const watcher = chokidar.watch('.', {
ignored: /(^|[/\\])\../, // ignore dotfiles
persistent: true,
});
watcher.on('add', () => batchChanges.newChange());
watcher.on('change', () => batchChanges.newChange());
watcher.on('unlink', () => batchChanges.newChange());
// Start server
app.listen(app.get('port'), function () {
context.print.log(`Development server listening on port ${options.port}`);
});
opener(`http://localhost:${options.port}`);
}
export default dev;

View File

@ -0,0 +1,35 @@
/*
Copyright 2020 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import fs from 'fs';
import path from 'path';
import loadGraphqlToCache from './loadGraphqlToCache';
import loadModule from '../../utils/loadModule';
async function getGraphql(context) {
const cleanVersion = context.version.replace(/[-.]/g, '_');
const cachePath = path.resolve(context.cacheDirectory, `scripts/graphql_${cleanVersion}`);
if (!fs.existsSync(path.resolve(cachePath, 'package/dist/remoteEntry.js'))) {
await loadGraphqlToCache(context, cachePath);
}
context.graphql = await loadModule(
path.resolve(cachePath, 'package/dist/moduleFederation'),
'./graphql'
);
return context;
}
export default getGraphql;

View File

@ -0,0 +1,49 @@
/*
Copyright 2020 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import axios from 'axios';
import decompress from 'decompress';
import decompressTargz from 'decompress-targz';
async function loadGraphqlToCache(context, cachePath) {
const registryUrl = 'https://registry.npmjs.org/@lowdefy/graphql';
const packageInfo = await axios.get(registryUrl);
if (!packageInfo || !packageInfo.data) {
// TODO: Check if user has internet connection.
throw new Error(
`Graphql package could not be found at ${registryUrl}. Check internet connection.`
);
}
if (!packageInfo.data.versions[context.version]) {
throw new Error(`Invalid Lowdefy version. Version "${context.version}" does not exist.`);
}
const tarball = await axios.get(packageInfo.data.versions[context.version].dist.tarball, {
responseType: 'arraybuffer',
});
if (!tarball || !tarball.data) {
/// TODO: Check if user has internet connection.
throw new Error(
`Graphql could not be fetched from ${
packageInfo.data.versions[context.version].dist.tarball
}. Check internet connection.`
);
}
await decompress(tarball.data, cachePath, {
plugins: [decompressTargz()],
});
}
export default loadGraphqlToCache;

View File

@ -0,0 +1,107 @@
/*
Copyright 2020 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*global __webpack_share_scopes__, __webpack_init_sharing__*/
import React from 'react';
import ReactDOM from 'react-dom';
import { Loading } from '@lowdefy/block-tools';
function loadComponent(scope, module) {
return async () => {
// Initializes the share scope. This fills it with known provided modules from this build and all remotes
await __webpack_init_sharing__('default');
const container = window[scope]; // or get the container somewhere else
// Initialize the container, it may provide shared modules
await container.init(__webpack_share_scopes__.default);
const factory = await window[scope].get(`./${module}`);
const Module = factory();
return Module;
};
}
const useDynamicScript = (args) => {
const [ready, setReady] = React.useState(false);
const [failed, setFailed] = React.useState(false);
React.useEffect(() => {
if (!args.url) {
return;
}
const element = document.createElement('script');
element.src = args.url;
element.type = 'text/javascript';
element.async = true;
setReady(false);
setFailed(false);
element.onload = () => {
console.log(`Dynamic Script Loaded: ${args.url}`);
setReady(true);
};
element.onerror = () => {
console.error(`Dynamic Script Error: ${args.url}`);
setReady(false);
setFailed(true);
};
document.head.appendChild(element);
return () => {
console.log(`Dynamic Script Removed: ${args.url}`);
document.head.removeChild(element);
};
}, [args.url]);
return {
ready,
failed,
};
};
function Shell({ version }) {
const { ready, failed } = useDynamicScript({
url: `http://unpkg.com/@lowdefy/renderer@${version}/dist/remoteEntry.js`,
});
if (!ready) {
return <Loading type="Spinner" properties={{ height: '100vh' }} />;
}
if (failed) {
return <h2>Failed to load dynamic script</h2>;
}
const Component = React.lazy(loadComponent('lowdefy_renderer', 'Renderer'));
return (
<React.Suspense fallback={<Loading type="Spinner" properties={{ height: '100vh' }} />}>
<Component />
</React.Suspense>
);
}
const getVersion = async () => {
return (await fetch(`api/dev/version`)).json();
};
getVersion().then((version) => {
ReactDOM.render(<Shell version={version} />, document.getElementById('root'));
});

View File

@ -0,0 +1,29 @@
<!--
Copyright 2020 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Lowdefy App</title>
<script src="/api/dev/reload.js"></script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="emotion"></div>
<div id="root"></div>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More