# Description

This is the builder for the sulu plugin ecosystem. It is essentially an node executable that bundles extensions with sulu and outputs an integrated ts repository, which can then be built like any other preact app. No modifications to sulu repository itself or any of the plugin directories are made, however it requires full read access to both.

Note that sulu should never be a dependency specified in your `package.json`, but you can have any other npm dependencies you desire, as long as they do not directly conflict sulu's dependencies. This builder forms a low-level override that will merge your files with sulu's into a single preact applet.

You can dry-import any sulu exports in your files with typecheck support despite them not being present until after the build. The exact mechanism is described in [build](#build). Effectively, while writing your code you will be making imports like `import {function a} from "sulu-base/@a/b"` - as if sulu was a dependency of your project - but the builder will, in the first stage of the build process, convert those imports to its relative equivalent - in this case `import {function a} from "/@a/b"`, which will make sense when your files are in the destination directory. Paths starting with /@ (ex /@components) are handled configured within sulu itself to be converted to a "./componens" with vite.

# Prerequisites

- `yarn`
- `node`
- a clone of the sulu repository (can be stored anywhere in the system, you only need to provide a path to it). I put it in sulu/ directory here, which is why it's part of the gitignore.

# Samples and integration tests

To sample the functionalities, two mock plugins are in the repository, mock_plugin_1_jsx and mock_plugin_2_tsx, former in js, latter in ts. View [the scripts section](#scripts) on how to run them.

Now follows a list of what each file in the integration tests plugins showcases.

- `mock_plugin_1_jsx/src/widgets/bare-mock-widget.widget.jsx` - that you can include the barest javascript plugin with no imports and have it recognized by sulu
- `mock_plugin_1_jsx/src/widgets/file-that-throws.jsx` - that you can exclude files by the excludePattern field in suluPluginManifest.json and not have it part of the output build.
- `mock_plugin_2_tsx/src/components/mock-clock-route.route.tsx` - that you can modify the router table of sulu. That you can relative-import from other plugin files.
- `integration_tests/mock_plugin_2_tsx/src/components/MockClockComponent.tsx` - that you can import from sulu itself. Use imports that require wrappers around <App> (in this case, `chakra-ui` depends on a <ChakraProvider> component which is not part of the plugin, but is present in sulu out of the box).
- `integration_tests/mock_plugin_2_tsx/src/widgets/clock-mock-widget.widget.tsx` - that you can add widgets and have them automatically included in the dashboard, or wherever else.
# Scripts

## init-plugin-repo

Initiates an empty plugin repository at a passed directory to start developing.

## build

Runs build. Needless to say $SULU_DIR, ... should be replaced with your parameters.

`yarn build --suluDir $SULU_DIR --writeDir $WRITE_DIR --pluginPaths $PLUGIN_PATHS -wvp`

Parameters:
- --suluDir - absolute or relative path to the sulu directory clone
- --writeDir - absolute or relative path to the write directory
- --writeFolder - (optional, defaults to `dist1`) folder to which the output will be written. Write path is obtained by joining the --suluDir and --writeFolder arguments. It *will be ensured empty, deleting contents if necessary*.
- --pluginPaths - comma-separated list of absolute or relative paths to plugin directories to integrate in the build, can be empty.
- -w - (optional) if set, the program will stop if the write directory exists and is not empty.
- -v - (optional) if set, the program will not stop if the --plugin-paths are empty.
- -p - (optional) if set, the build is incremental - the directory is not cleared, and only files with newer timestamps for a given plugin/base sulu (or new files) are updated. This is a development-friendly option because it enables smooth watch mode for vite preact.
- -s - (optional) if set, suppresses logging messages. Errors are not affected.
- -t - (optional) if set, process will watch for file changes in sulu directory and all plugin paths, and re-run the build on any changes. `node_modules, .git, yarn.lock, yarn-erorr.log` are not watched. Use alongside -p for a smooth development setup. Secondary terminal needs to be running `yarn dev` to re-build the result. Note that you still need to kill the secondary terminal and run `yarn` if you make changes to dependency list. Note that, as the vite build watcher is not prepared to handle multi-file resets too frequently, if you hold down the save key, or make too many changes, I have noticed that it can results in a race conditiong of it trying to re-run the server on port 3000 before closing the previous instance on 3000, resulting in it opening on port 3001 before closing 3000, then repeating on higher ports, etc. So if browser suddenly sees nothing on 3000 you have to reset the dev server. There is nothing I can do about it.

## test

Run the tests. Requires dev dependencies to run.

## test-build-compile

Runs the integration test for the compilation phase, which runs a build on plugins from within integration_tests/*. Requires sulu repository under sulu/, dev dependencies installed. Compiles into ts1-result/.

## test-build-run-dev-result

Runs a dev build on the integration test compilation result on host 3000.

## test-build

Runs test-build-compile and test-build-run-dev-result.
## Plugin repositories

Each plugin repository, the root of which is identified from `PLUGIN_PATHS` environment variable, is expected to have a `suluPluginManifest.json` file containing the following fields:

- `pattern` - a REGEX that should match all file and directory paths (*and their parent paths, absolutely*) of files you wish to be integrated with sulu. Note that all of the critical configuration files are excluded from the merge regardless of them matching the pattern. Note that it does not extend to cdn lockfiles or version control program files, so make sure those are not matched. Important: this regex *must* match all parent paths to files, and the directory match regex of type `/sth/**/.*\.extension` *will not work*, as it is not recognized by the default js regex engine. Some sample regexes to use:
  - `(/.*)*(/.*\\.(ts|tsx))?` will match all files and their parents with ts and tsx extensions.
  - `(/.*)*(/src)(/.*)*((/.*\\.(ts|tsx))?` as your file structure abides by sulu's, all relevant files are found under src/. This regex matches all ts and tsx files whose root is src/.
- `excludePattern` - (optional) Any file matched by `pattern` that also matches excludePattern will be treated as if it did not match. If not provided, defaults to `(?!)` - a regex that matches nothing. If you'd like to have a dummy field rather than ommiting the key, just set it to `(?!)`.
- `integrate_package_file_dependencies` - if equal to `true`, the non-dev dependencies specified in the `package.json` file will be considered when building the `node_modules` of the output ts and js repositories.
- `integrate_package_file_dev_dependencies` - if equal to `true`, the dev dependencies specified in the `package.json` file will be considered when building the `node_modules` of the output ts repository. This can be useful for including type definition dependencies that would otherwise not be considered in the compilation.

### Critical files

The following files from plugin directories are treated differently than was described. They will not be matched by the `pattern` spacified, and may or may not be read.

- `.env` - .env files of all plugins and of sulu are appended into a single output file. While in incremental build mode, a .env is only overriden if any of its entries change (as compared to a new candidate).
- `suluPluginManifest.json` - As [described above](#plugin-repositories).
- `package.json` - If present, its contents will be read and considered according to the configuration file settings as [described above](#plugin-repositories).
- `yarn.lock` - Ignored.

# Build

1. Clear the destination directory specified by `SULU_BUILDER_OUTPUT_1` variable. If `PRESERVE_NODE_MODULES` env variable is set then `node_modules` directory within it are preserved instead.
2. Make a deep copy of the sulu repository into the destination directory, with a few exceptions (.git, node_modules). Note that `yarn.lock` is copied.
3. For each, if any, of the plugin directories read from the `SULU_PATH` env variable:
  - Walk all files matched by the `pattern` AND not matched by `excludePattern` values of `suluPluginManifest.json` (relative to the plugin directory path) (excluding [critical files](#critical-files)) and copy them directly into the destination directory, overriding if necessary, flag overrides in console.
  - In every transferred file of extension `.tsx`, `.ts`, `.js`, `.jsx`, replace occurences of `sulu-base` imports with direct imports. For example, this means that if you write an import like `import {a} from "sulu-base/@component/b"`, in the destination file this will read `import {a} from "/@component/b"`.
  - If `integrate_package_file_dependencies` is equal to `true`, plugin directory's `package.json` is integrated into the `package.json` of the output directory. Primitive (string, number) from sulu's package.json take precedence (ex. if there is a script of the same name, or a dependency of the same name, sulu's will *not* be overriden). Arrays and objects are otherwise merged (rule from previous sentence still applies). Greater-version-than (^X.X.X) plugin dependencies are converted to strict version (=X.X.X) in plugins due to plugin lockfiles not being merged. Merging them would cause more issues than it's worth.
4. Run `yarn` on the destination directory.

At the end, the output directory will contain a self-consistent preact repository, though you will not know whether there are no errors until you running the proper preact build within it.

# Plugin development

Sulu is designed to look for files matching certain names and expects them to have default exports of a certain structure. This means you can do things like adding widgets, modifying styles, modifying routing etc without having to worry about overriding any files - they will be automatically read and interpreted on runtime. For the documentation on that go to sulu's README, as a copy will not be maintained here.

# Sample build script

Footnote, this is what I have scripted in my .bashrc to automate the build of sulu-multiap plugin. Replace the paths to match yours, of course.

```
export S_BUILDER="$HOME/repos/sulu-builder"
export S_SULU="$HOME/repos/sulu-builder/sulu"
export S_MULTIAP="$HOME/repos/sulu-multi-ap"
export S_MULTIAP_RESULT="$HOME/repos/multiap-build"

<!-- The below command is suited to a clean cli build. It makes a build from scratch, without caching node_modules or yarn.lock, thus it takes the full minute or so to build every time. -->
alias build_multiap="cd $S_BUILDER && yarn build --suluDir $S_SULU --pluginPaths=$S_MULTIAP --writeDir=$S_MULTIAP_RESULT"
<!-- this one watches for file changes and re-runs the build, preserving node_modules, .env, and yarn.lock. First run takes as long as a clean build, but consecutive are as fast as it gets. If making changes to the .env, or deleting existing files, you need to run a clean build. -->
alias w_build_multiap="cd $S_BUILDER && yarn build -vpto --suluDir $S_SULU --pluginPaths=$S_MULTIAP --writeDir=$S_MULTIAP_RESULT"
```

# Sulu modules

This repository depends on sulu, sulu-lcm, sulu-multi-ap, sulu-theme-iopsys repositories as submodules, but only for the gitlab build pipeline.
Modules with there target versions defined in [modules.mk|./modules.mk] file.
