Merge branch 'develop' into build-operators

This commit is contained in:
Gerrie van Wyk 2022-03-16 17:45:12 +02:00 committed by GitHub
commit 466e61f7c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 263 additions and 145 deletions

2
.gitignore vendored
View File

@ -32,3 +32,5 @@ packages/utils/node-utils/test/writeFile/writeFile.txt
packages/utils/node-utils/test/copyDirectory/**
app/**
packages/server-dev/plugins/**
packages/server/plugins/**

3
.pnp.cjs generated
View File

@ -3232,6 +3232,9 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"packageLocation": "./packages/docs/",
"packageDependencies": [
["@lowdefy/docs", "workspace:packages/docs"],
["@jest/globals", "npm:27.5.1"],
["@lowdefy/helpers", "workspace:packages/utils/helpers"],
["@swc/cli", "virtual:babee6e81435a5d101529cd67f2c6b175f4db37a4ab0b58df15adf73dd11be8917ac14caf44ab4e6882a92c61661055072365b349016e85173e049f006fc2305#npm:0.1.55"],
["@swc/core", "npm:1.2.135"],
["@swc/jest", "virtual:babee6e81435a5d101529cd67f2c6b175f4db37a4ab0b58df15adf73dd11be8917ac14caf44ab4e6882a92c61661055072365b349016e85173e049f006fc2305#npm:0.2.17"],
["jest", "virtual:babee6e81435a5d101529cd67f2c6b175f4db37a4ab0b58df15adf73dd11be8917ac14caf44ab4e6882a92c61661055072365b349016e85173e049f006fc2305#npm:27.5.1"]

View File

@ -45,8 +45,8 @@ function getConfigIcons({ components, icons, regex }) {
}
function getBlockDefaultIcons({ components, context, icons, regex }) {
Object.entries(components.types.blocks).forEach(([blockName, block]) => {
context.typesMap.icons[block.package][blockName].forEach((icon) => {
Object.keys(components.types.blocks).forEach((blockName) => {
(context.typesMap.icons[blockName] || []).forEach((icon) => {
[...JSON.stringify(icon).matchAll(regex)].map((match) => icons.add(match[1]));
});
});

View File

@ -20,12 +20,12 @@ function buildStyles({ components, context }) {
Object.entries(components.types.blocks).forEach(([blockName, block]) => {
styles.add(
...(context.typesMap.styles[block.package].default || []).map(
...(context.typesMap.styles.packages[block.package] || []).map(
(style) => `${block.package}/${style}`
)
);
styles.add(
...(context.typesMap.styles[block.package][blockName] || []).map(
...(context.typesMap.styles.blocks[blockName] || []).map(
(style) => `${block.package}/${style}`
)
);

View File

@ -28,6 +28,7 @@ function buildTypeClass(
throw new Error(`${typeClass} type "${typeName}" was used but is not defined.`);
}
store[typeName] = {
originalTypeName: definitions[typeName].originalTypeName,
package: definitions[typeName].package,
version: definitions[typeName].version,
count: counts[typeName],

View File

@ -16,19 +16,19 @@
import { nunjucksFunction } from '@lowdefy/nunjucks';
const template = `{%- for import in imports -%}
import { {{ import.type }} } from '{{ import.package }}/{{ importPath }}';
import { {{ import.originalTypeName }} as {{ import.typeName }} } from '{{ import.package }}/{{ importPath }}';
{% endfor -%}
export default {
{% for import in imports -%}
{{ import.type }},
{{ import.typeName }},
{% endfor -%}
}`;
};`;
function generateImportFile({ types, importPath }) {
const templateFn = nunjucksFunction(template);
const imports = Object.keys(types).map((type) => ({
type,
...types[type],
const imports = Object.keys(types).map((typeName) => ({
typeName,
...types[typeName],
}));
return templateFn({ imports, importPath });
}

View File

@ -57,7 +57,10 @@ async function generateDefaultTypesMap() {
server: {},
},
requests: {},
styles: {},
styles: {
packages: {},
blocks: {},
},
};
await Promise.all(

View File

@ -21,6 +21,7 @@ function createTypeDefinitions({ packageName, store, typeNames, typePrefix, vers
typeNames.forEach((typeName) => {
store[`${typePrefix}${typeName}`] = {
package: packageName,
originalTypeName: typeName,
version,
};
});
@ -77,11 +78,19 @@ function createPluginTypesMap({ packageName, packageTypes, typePrefix = '', type
});
if (type.isObject(packageTypes.styles)) {
typesMap.styles[packageName] = packageTypes.styles;
Object.entries(packageTypes.styles).forEach(([blockType, styles]) => {
if (blockType === 'default') {
typesMap.styles.packages[packageName] = styles;
} else {
typesMap.styles.blocks[`${typePrefix}${blockType}`] = styles;
}
});
}
if (type.isObject(packageTypes.icons)) {
typesMap.icons[packageName] = packageTypes.icons;
Object.entries(packageTypes.icons).forEach(([blockType, icons]) => {
typesMap.icons[`${typePrefix}${blockType}`] = icons;
});
}
}

View File

@ -15,9 +15,9 @@
*/
import addCustomPluginsAsDeps from '../../utils/addCustomPluginsAsDeps.js';
import copyPluginsFolder from '../../utils/copyPluginsFolder.js';
import getServer from '../../utils/getServer.js';
import installServer from '../../utils/installServer.js';
import mergePackageJson from '../../utils/mergePackageJson.js';
import runLowdefyBuild from '../../utils/runLowdefyBuild.js';
import runNextBuild from '../../utils/runNextBuild.js';
@ -25,10 +25,7 @@ async function build({ context }) {
context.print.info('Starting build.');
const directory = context.directories.server;
await getServer({ context, packageName: '@lowdefy/server', directory });
await mergePackageJson({
context,
serverDirectory: directory,
});
await copyPluginsFolder({ context, directory });
await addCustomPluginsAsDeps({ context, directory });
await installServer({ context, directory });
await runLowdefyBuild({ context, directory });

View File

@ -15,8 +15,8 @@
*/
import addCustomPluginsAsDeps from '../../utils/addCustomPluginsAsDeps.js';
import copyPluginsFolder from '../../utils/copyPluginsFolder.js';
import installServer from '../../utils/installServer.js';
import mergePackageJson from '../../utils/mergePackageJson.js';
import runDevServer from './runDevServer.js';
import getServer from '../../utils/getServer.js';
@ -24,10 +24,7 @@ async function dev({ context }) {
const directory = context.directories.dev;
context.print.info('Starting development server.');
await getServer({ context, packageName: '@lowdefy/server-dev', directory });
await mergePackageJson({
context,
serverDirectory: directory,
});
await copyPluginsFolder({ context, directory });
await addCustomPluginsAsDeps({ context, directory });
await installServer({ context, directory });
context.sendTelemetry();

View File

@ -21,7 +21,7 @@ import runStart from './runStart.js';
async function build({ context }) {
context.print.info('Starting server.');
context.sendTelemetry({ sendTypes: true });
await runStart({ context, directory: context.directory.server });
await runStart({ context, directory: context.directories.server });
}
export default build;

View File

@ -0,0 +1,31 @@
/*
Copyright 2020-2021 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 fs from 'fs';
import { copyDirectory } from '@lowdefy/node-utils';
async function copyPluginsFolder({ context, directory }) {
if (context.directories.config === directory) return;
if (!fs.existsSync(path.resolve(context.directories.config, 'plugins'))) return;
await copyDirectory(
path.resolve(context.directories.config, 'plugins'),
path.resolve(directory, 'plugins')
);
}
export default copyPluginsFolder;

View File

@ -1,34 +0,0 @@
/*
Copyright 2020-2021 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 { mergeObjects } from '@lowdefy/helpers';
import { readFile, writeFile } from '@lowdefy/node-utils';
async function mergePackageJson({ context, serverDirectory }) {
const serverPackageJsonPath = path.join(serverDirectory, 'package.json');
const serverPackageJson = JSON.parse(await readFile(serverPackageJsonPath));
const configPackageJson = JSON.parse(
await readFile(path.join(context.directories.config, 'package.json'))
);
const mergedPackageJson = mergeObjects([serverPackageJson, configPackageJson]);
const mergedPackageJsonContent = JSON.stringify(mergedPackageJson, null, 2).concat('\n');
await writeFile(serverPackageJsonPath, mergedPackageJsonContent);
}
export default mergePackageJson;

View File

@ -1 +0,0 @@
<script src="https://cdn.jsdelivr.net/npm/docsearch.js@2.6.3/dist/cdn/docsearch.min.js"></script>

View File

@ -1,4 +1,3 @@
<script type="module" src="/public/modules/index.js"></script>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/docsearch.js@2.6.3/dist/cdn/docsearch.min.css"

View File

@ -1,23 +1,13 @@
export default {
clearMocks: true,
collectCoverage: true,
collectCoverageFrom: ['**/*.js'],
collectCoverageFrom: ['src/**/*.js'],
coverageDirectory: 'coverage',
coveragePathIgnorePatterns: [
'<rootDir>/.lowdefy/',
'<rootDir>/jest.config.js',
'<rootDir>/coverage/',
'<rootDir>/howto/',
],
coveragePathIgnorePatterns: ['<rootDir>/dist/', '<rootDir>/src/operatorsClient.js'],
coverageReporters: [['lcov', { projectRoot: '../..' }], 'text', 'clover'],
errorOnDeprecated: true,
testEnvironment: 'node',
testPathIgnorePatterns: [
'<rootDir>/.lowdefy/',
'<rootDir>/jest.config.js',
'<rootDir>/coverage/',
'<rootDir>/howto/',
],
testPathIgnorePatterns: ['<rootDir>/dist/'],
transform: {
'^.+\\.(t|j)sx?$': ['@swc/jest', { configFile: '../../.swcrc.test' }],
},

View File

@ -19,6 +19,10 @@ licence: Apache-2.0
cli:
disableTelemetry: true
plugins:
- name: '@lowdefy/docs'
version: '4.0.0-alpha.7'
global:
all_icons:
_ref: blocks/all_icons.yaml
@ -30,8 +34,6 @@ app:
html:
appendHead:
_ref: head.html
appendBody:
_ref: body.html
connections:
- id: discord_channel

View File

@ -27,11 +27,27 @@
"url": "https://github.com/lowdefy/lowdefy.git"
},
"type": "module",
"exports": {
"./actions": "./dist/actions.js",
"./operators/client": "./dist/operatorsClient.js",
"./types": "./dist/types.js"
},
"files": [
"dist/*"
],
"scripts": {
"build": "yarn swc",
"clean": "rm -rf .lowdefy",
"test": "jest --coverage"
"prepare": "yarn build",
"swc": "swc src --out-dir dist --config-file ../../.swcrc --delete-dir-on-start",
"test": "yarn node --experimental-vm-modules $(yarn bin jest)"
},
"dependencies": {
"@lowdefy/helpers": "4.0.0-alpha.7"
},
"devDependencies": {
"@jest/globals": "27.5.1",
"@swc/cli": "0.1.55",
"@swc/core": "1.2.135",
"@swc/jest": "0.2.17",
"jest": "27.5.1"

View File

@ -1,5 +0,0 @@
import connectDocsearch from './connectDocsearch.js';
import filterDefaultValue from './filterDefaultValue.js';
window.lowdefy.registerJsAction('connectDocsearch', connectDocsearch);
window.lowdefy.registerJsOperator('filterDefaultValue', filterDefaultValue);

View File

@ -14,22 +14,24 @@
limitations under the License.
*/
function filterDefaultValue(value, defaultValue) {
const isObject = (obj) => typeof obj === 'object' && obj !== null && !Array.isArray(obj);
const isEmptyObject = (obj) => isObject(obj) && Object.keys(obj).length === 0;
import { type } from '@lowdefy/helpers';
function custom_filter_default_value({ params }) {
const { value, defaultValue } = params;
const isEmptyObject = (obj) => type.isObject(obj) && Object.keys(obj).length === 0;
const getNestedValue = (obj, path) => {
const keys = [...path];
const key = keys.shift();
const value = obj[key];
if (keys.length > 0 && isObject(value)) return getNestedValue(value, keys);
if (keys.length > 0 && type.isObject(value)) return getNestedValue(value, keys);
return value;
};
const filterObject = ({ obj, path }) => {
Object.keys(obj).forEach((key) => {
const propPath = path.concat([key]);
if (isObject(obj[key])) {
if (type.isObject(obj[key])) {
filterObject({ obj: obj[key], path: propPath });
}
const dv = getNestedValue(defaultValue, propPath);
@ -45,4 +47,4 @@ function filterDefaultValue(value, defaultValue) {
return filterObject({ obj: value, path: [] });
}
export default filterDefaultValue;
export default custom_filter_default_value;

View File

@ -14,11 +14,11 @@
limitations under the License.
*/
import filterDefaultValue from '../filterDefaultValue';
import filter_default_value from './filter_default_value.js';
test('no default value', () => {
const value = { a: 1 };
expect(filterDefaultValue(value, {})).toEqual({ a: 1 });
expect(filter_default_value({ params: { value, defaultValue: {} } })).toEqual({ a: 1 });
});
test('remove null, empty objects and empty objects with all properties null or empty', () => {
@ -30,7 +30,7 @@ test('remove null, empty objects and empty objects with all properties null or e
g: { h: { i: { j: null, k: { l: null } } } },
m: { n: null, o: null, p: null },
};
expect(filterDefaultValue(value, {})).toEqual({});
expect(filter_default_value({ params: { value, defaultValue: {} } })).toEqual({});
});
test('remove a default value', () => {
@ -42,7 +42,7 @@ test('remove a default value', () => {
a: 1,
b: 1,
};
expect(filterDefaultValue(value, defaultValue)).toEqual({ b: 2 });
expect(filter_default_value({ params: { value, defaultValue } })).toEqual({ b: 2 });
});
test('remove a default value but keep arrays', () => {
@ -64,7 +64,7 @@ test('remove a default value but keep arrays', () => {
},
e: ['1', '2'],
};
expect(filterDefaultValue(value, defaultValue)).toEqual({
expect(filter_default_value({ params: { value, defaultValue } })).toEqual({
a: {
c: false,
d: true,
@ -87,5 +87,5 @@ test('only recurse getNestedValue on objects', () => {
b: null,
},
};
expect(filterDefaultValue(value, defaultValue)).toEqual({ a: { b: { c: 1 } } });
expect(filter_default_value({ params: { value, defaultValue } })).toEqual({ a: { b: { c: 1 } } });
});

View File

@ -0,0 +1,17 @@
/*
Copyright 2020-2021 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.
*/
export { default as _custom_filter_default_value } from './operators/client/filter_default_value.js';

View File

@ -0,0 +1,23 @@
/*
Copyright 2020-2021 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 * as operatorsClient from './operatorsClient.js';
export default {
operators: {
client: Object.keys(operatorsClient),
},
};

View File

@ -192,14 +192,16 @@ areas:
required:
_state: block.required
properties:
_js.filterDefaultValue:
- _ref:
_custom_filter_default_value:
value:
_ref:
path:
_var: schema
vars:
block_type: {{ block_type }}
transformer: templates/blocks/propertiesGetterTransformer.js
- _ref:
defaultValue:
_ref:
path:
_var: schema
transformer: templates/blocks/defaultValueTransformer.js
@ -309,8 +311,9 @@ areas:
options:
sortKeys: false
on:
_js.filterDefaultValue:
- id: block_id
_custom_filter_default_value:
value:
id: block_id
type: {{ block_type }}
required:
_state: block.required
@ -330,7 +333,8 @@ areas:
vars:
block_type: {{ block_type }}
transformer: templates/blocks/propertiesGetterTransformer.js
- required: false
defaultValue:
required: false
visible: true
layout:
align: top

View File

@ -13,6 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
import * as actions from './actions.js';
export default {

View File

@ -36,11 +36,13 @@ const require = createRequire(import.meta.url);
async function getContext() {
const env = process.env;
const nextPageJson = require('next/package.json');
const context = {
bin: {
// TODO: The string replace is a little hacky and will fail if the location of the bin changes,
lowdefyBuild: require.resolve('@lowdefy/build').replace('index.js', 'scripts/run.js'),
next: require.resolve('next').replace('server/next.js', 'bin/next'),
next: path.join(
require.resolve('next').replace(nextPageJson.main.substring(1), ''),
nextPageJson.bin.next
),
},
directories: {
build: path.resolve(process.cwd(), './build'),
@ -56,7 +58,7 @@ async function getContext() {
argv.watchIgnore || env.LOWDEFY_SERVER_DEV_WATCH_IGNORE
? JSON.parse(env.LOWDEFY_SERVER_DEV_WATCH_IGNORE)
: [],
// TODO: read option from from env
// TODO: read option from env
verbose: argv.verbose || false,
},
packageManager: argv.packageManager || env.LOWDEFY_PACKAGE_MANAGER || 'npm',

View File

@ -14,24 +14,17 @@
limitations under the License.
*/
import { spawnProcess } from '@lowdefy/node-utils';
import build from '@lowdefy/build';
import createCustomPluginTypesMap from '../utils/createCustomPluginTypesMap.mjs';
function lowdefyBuild({ bin, directories, options }) {
function lowdefyBuild({ directories, options }) {
return async () => {
await spawnProcess({
command: 'node',
args: [bin.lowdefyBuild],
const customTypesMap = await createCustomPluginTypesMap({ directories });
await build({
customTypesMap,
directories,
logger: console,
processOptions: {
env: {
...process.env,
LOWDEFY_BUILD_REF_RESOLVER: options.refResolver,
LOWDEFY_DIRECTORY_BUILD: directories.build,
LOWDEFY_DIRECTORY_CONFIG: directories.config,
LOWDEFY_DIRECTORY_SERVER: process.cwd(),
},
},
silent: false,
refResolver: options.refResolver,
});
};
}

View File

@ -0,0 +1,66 @@
#!/usr/bin/env node
/*
Copyright 2020-2021 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 { get } from '@lowdefy/helpers';
import { readFile } from '@lowdefy/node-utils';
import { createPluginTypesMap } from '@lowdefy/build';
import YAML from 'yaml';
async function getPluginDefinitions({ directories }) {
let lowdefyYaml = await readFile(path.join(directories.config, 'lowdefy.yaml'));
if (!lowdefyYaml) {
lowdefyYaml = await readFile(path.join(directories.config, 'lowdefy.yml'));
}
const lowdefy = YAML.parse(lowdefyYaml);
return get(lowdefy, 'plugins', { default: [] });
}
async function createCustomPluginTypesMap({ directories }) {
const customTypesMap = {
actions: {},
blocks: {},
connections: {},
icons: {},
operators: {
client: {},
server: {},
},
requests: {},
styles: {
packages: {},
blocks: {},
},
};
const pluginDefinitions = await getPluginDefinitions({ directories });
for (const plugin of pluginDefinitions) {
const { default: types } = await import(`${plugin.name}/types`);
createPluginTypesMap({
packageTypes: types,
typesMap: customTypesMap,
packageName: plugin.name,
version: plugin.version,
typePrefix: plugin.typePrefix,
});
}
return customTypesMap;
}
export default createCustomPluginTypesMap;

View File

@ -32,8 +32,6 @@
".eslintrc.yaml"
],
"scripts": {
"build:lowdefy": "lowdefy-build",
"build:next": "next build",
"start": "node manager/run.mjs",
"lint": "next lint",
"next": "next"

View File

@ -14,7 +14,7 @@
limitations under the License.
*/
import React from 'react';
import React, { Suspense } from 'react';
import dynamic from 'next/dynamic';
import { ErrorBoundary } from '@lowdefy/block-utils';
@ -27,11 +27,13 @@ const lowdefy = {};
function App({ Component, pageProps }) {
return (
<ErrorBoundary>
<LowdefyContext lowdefy={lowdefy}>
<Component lowdefy={lowdefy} {...pageProps} />
</LowdefyContext>
</ErrorBoundary>
<Suspense fallback="">
<ErrorBoundary>
<LowdefyContext lowdefy={lowdefy}>
<Component lowdefy={lowdefy} {...pageProps} />
</LowdefyContext>
</ErrorBoundary>
</Suspense>
);
}

View File

@ -19,8 +19,7 @@ import path from 'path';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import build from '@lowdefy/build';
import createCustomTypesMap from './createCustomTypesMap.mjs';
import build, { createCustomPluginTypesMap } from '@lowdefy/build';
const argv = yargs(hideBin(process.argv)).argv;
@ -39,8 +38,7 @@ async function run() {
),
};
const customTypesMap = await createCustomTypesMap({ directories });
console.log(customTypesMap);
const customTypesMap = await createCustomPluginTypesMap({ directories });
await build({
customTypesMap,
directories,

View File

@ -41,25 +41,24 @@ async function createCustomTypesMap({ directories }) {
server: {},
},
requests: {},
styles: {},
styles: {
packages: {},
blocks: {},
},
};
const pluginDefinitions = await getPluginDefinitions({ directories });
// TODO: Prefixes in icons and styles
// TODO: Should be resolved in order
await Promise.all(
pluginDefinitions.map(async (plugin) => {
const { default: types } = await import(`${plugin.name}/types`);
createPluginTypesMap({
packageTypes: types,
typesMap: customTypesMap,
packageName: plugin.name,
version: plugin.version,
typePrefix: plugin.typePrefix,
});
})
);
for (const plugin of pluginDefinitions) {
const { default: types } = await import(`${plugin.name}/types`);
createPluginTypesMap({
packageTypes: types,
typesMap: customTypesMap,
packageName: plugin.name,
version: plugin.version,
typePrefix: plugin.typePrefix,
});
}
return customTypesMap;
}

View File

@ -2372,6 +2372,9 @@ __metadata:
version: 0.0.0-use.local
resolution: "@lowdefy/docs@workspace:packages/docs"
dependencies:
"@jest/globals": 27.5.1
"@lowdefy/helpers": 4.0.0-alpha.7
"@swc/cli": 0.1.55
"@swc/core": 1.2.135
"@swc/jest": 0.2.17
jest: 27.5.1