Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ For detailed architecture, development guidelines, and contribution documentatio
## Projects

1. **eav-ui** main Angular project. Build using `ng`
1. **field-custom-gps** an extension field with gps-picker & map. Build using `webpack`
1. **field-string-wysiwyg** an extension field for wysiwyg. Build using `webpack`
1. **field-custom-gps** an extension field with gps-picker & map. Build using `esbuild`
1. **field-string-wysiwyg** an extension field for wysiwyg. Build using `esbuild`

There are various projects in here, some building with angular `ng build` and others directly with webpack.
The main application uses Angular's application builder; the extension fields use esbuild.

## Building the Main Angular Project

Expand All @@ -22,13 +22,13 @@ To build, use the normal `ng` syntax, like `ng build` or `ng build --watch`

For more guidance on building and deploying to Dnn/Oqtane, see <https://go.2sxc.org/build>

## Building Webpack projects
## Building extension projects

Just run `webpack --env parts=PARTNAME` where PARTNAME is `wysiwyg`, `gps`, `all` (which is like using `'wysiwyg,gps'`).
Run `node ./build-helpers/build-parts.js --parts=PARTNAME`, where PARTNAME is `wysiwyg`, `gps`, `all`, or a comma-separated combination.

You can also use `--watch` like `webpack --env parts=all --watch`
Add `--watch` for continuous builds, for example `node ./build-helpers/build-parts.js --parts=all --watch`.

You can also use `--mode=production` like `webpack --env parts=wysiwyg --mode=production`
Add `--production` for minified output with externally hosted source maps.

## Dev info

Expand Down
24 changes: 14 additions & 10 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"builder": "@angular/build:application",
"options": {
"outputPath": "dist/eav-ui",
"outputPath": {
"base": "dist/eav-ui",
"browser": ""
},
"index": "projects/eav-ui/src/index-raw.html",
"main": "projects/eav-ui/src/main.ts",
"polyfills": "projects/eav-ui/src/polyfills.ts",
"browser": "projects/eav-ui/src/main.ts",
"polyfills": ["projects/eav-ui/src/polyfills.ts"],
"tsConfig": "projects/eav-ui/tsconfig.app.json",
"inlineStyleLanguage": "css",
"assets": ["projects/eav-ui/src/assets"],
Expand All @@ -37,6 +40,11 @@
"projects/eav-ui/src/app/edit/assets/scripts/dropzone-dragging-helper.js",
"projects/eav-ui/src/app/edit/assets/scripts/clipboard-paste/paste.js"
],
"loader": {
".png": "file",
".rawts": "text",
".svg": "text"
},
"allowedCommonJsDependencies": ["dayjs"]
},
"configurations": {
Expand All @@ -51,17 +59,13 @@
"outputHashing": "bundles",
"sourceMap": true,
"namedChunks": true,
"extractLicenses": true,
"vendorChunk": true,
"buildOptimizer": true
"extractLicenses": true
},
"development": {
"optimization": false,
"sourceMap": true,
"namedChunks": true,
"extractLicenses": false,
"vendorChunk": true,
"buildOptimizer": false
"extractLicenses": false
}
},
"defaultConfiguration": "development"
Expand Down
122 changes: 122 additions & 0 deletions build-helpers/build-parts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
const fs = require('node:fs');
const path = require('node:path');
const { fileURLToPath } = require('node:url');
const esbuild = require('esbuild');
const sass = require('sass');
const buildConfig = require('../../2sxc-ui/packages/2sxc-load-build-config').BuildConfig;

const root = path.resolve(__dirname, '..');
const args = new Set(process.argv.slice(2));
const partsArg = process.argv.find(arg => arg.startsWith('--parts='));
const production = args.has('--production');
const watch = args.has('--watch');
const copy = !args.has('--no-copy');
const selected = (partsArg?.slice('--parts='.length) ?? '').split(',');
const parts = selected.includes('all') ? ['wysiwyg', 'gps'] : selected;

if (!partsArg || parts.some(part => !['wysiwyg', 'gps'].includes(part))) {
throw new Error('Specify --parts=all, --parts=wysiwyg, --parts=gps, or a comma-separated combination.');
}

const definitions = {
gps: {
project: 'field-custom-gps',
entries: ['src/main/main.ts', 'src/preview/preview.ts'],
},
wysiwyg: {
project: 'field-string-wysiwyg',
entries: [
'src/field-string-wysiwyg/field-string-wysiwyg.ts',
'src/field-string-wysiwyg/field-string-wysiwyg-preview.ts',
'src/field-string-wysiwyg/field-string-wysiwyg-editor.ts',
],
copy: [
['src/i18n', 'i18n'],
['src/assets/2sxc-tinymce-skin', '.'],
],
},
};

const sassTextPlugin = {
name: 'sass-as-text',
setup(build) {
build.onLoad({ filter: /\.s[ac]ss$/ }, async ({ path: file }) => {
const result = await sass.compileAsync(file, { loadPaths: [path.dirname(file)] });
return { contents: result.css, loader: 'text', watchFiles: [...result.loadedUrls].map(fileURLToPath) };
});
},
};

function copyDirectory(source, target) {
if (fs.existsSync(source)) fs.cpSync(source, target, { recursive: true, force: true });
}

function copyToTargets(output, project) {
for (const target of [...buildConfig.Sources, ...buildConfig.JsTargets]) {
copyDirectory(output, path.join(target, 'extensions', project));
}
}

async function buildPart(name) {
const definition = definitions[name];
const projectRoot = path.join(root, 'projects', definition.project);
const output = path.join(root, 'dist', 'extensions', definition.project);
fs.rmSync(output, { recursive: true, force: true });
fs.mkdirSync(output, { recursive: true });

const copyAssets = () => {
for (const [source, target] of definition.copy ?? []) {
copyDirectory(path.join(projectRoot, source), path.join(output, target));
}
};

const afterBuildPlugin = {
name: 'copy-build-output',
setup(build) {
build.onEnd(result => {
if (result.errors.length)
return;
copyAssets();
if (production) {
const bundle = path.join(output, 'index.js');
const sourceMapUrl = `https://sources.2sxc.org/${require('../package.json').version}/extensions/${definition.project}/index.js.map`;
fs.appendFileSync(bundle, `\n//# sourceMappingURL=${sourceMapUrl}\n`);
}
if (copy) copyToTargets(output, definition.project);
console.log(`Built ${name} to ${path.relative(root, output)}`);
});
},
};

const context = await esbuild.context({
absWorkingDir: projectRoot,
stdin: {
contents: definition.entries.map(entry => `import './${entry.replaceAll('\\', '/')}';`).join('\n'),
resolveDir: projectRoot,
sourcefile: `${name}-entries.ts`,
loader: 'ts',
},
outfile: path.join(output, 'index.js'),
bundle: true,
define: { __PRODUCTION__: JSON.stringify(production) },
loader: { '.html': 'text', '.css': 'text', '.svg': 'text', '.rawts': 'text' },
minify: production,
plugins: [sassTextPlugin, afterBuildPlugin],
sourcemap: production ? 'external' : true,
sourcesContent: !production,
target: 'es2022',
tsconfig: path.join(projectRoot, 'tsconfig.json'),
});

if (watch)
await context.watch();
else {
await context.rebuild();
await context.dispose();
}
}

Promise.all(parts.map(buildPart)).catch(error => {
console.error(error);
process.exitCode = 1;
});
38 changes: 0 additions & 38 deletions build-helpers/external-source-maps-elements.js

This file was deleted.

29 changes: 0 additions & 29 deletions build-helpers/multi-output.js

This file was deleted.

Loading