diff --git a/.editorconfig b/.editorconfig index 9c66b9c..23eebaa 100644 --- a/.editorconfig +++ b/.editorconfig @@ -21,6 +21,6 @@ indent_style = space [*.md] trim_trailing_whitespace = false -[{fixtures,examples,newline-fixtures,async-fixtures}/**/*] +[{fixtures,examples,newline_fixtures,async_fixtures}/**/*] insert_final_newline = ignore indent_style = space diff --git a/.github/COMMIT_CONVENTION.md b/.github/COMMIT_CONVENTION.md deleted file mode 100644 index fc852af..0000000 --- a/.github/COMMIT_CONVENTION.md +++ /dev/null @@ -1,70 +0,0 @@ -## Git Commit Message Convention - -> This is adapted from [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular). - -Using conventional commit messages, we can automate the process of generating the CHANGELOG file. All commits messages will automatically be validated against the following regex. - -``` js -/^(revert: )?(feat|fix|docs|style|refactor|perf|test|workflow|ci|chore|types|build|improvement)((.+))?: .{1,50}/ -``` - -## Commit Message Format -A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**: - -> The **scope** is optional - -``` -feat(router): add support for prefix - -Prefix makes it easier to append a path to a group of routes -``` - -1. `feat` is type. -2. `router` is scope and is optional -3. `add support for prefix` is the subject -4. The **body** is followed by a blank line. -5. The optional **footer** can be added after the body, followed by a blank line. - -## Types -Only one type can be used at a time and only following types are allowed. - -- feat -- fix -- docs -- style -- refactor -- perf -- test -- workflow -- ci -- chore -- types -- build - -If a type is `feat`, `fix` or `perf`, then the commit will appear in the CHANGELOG.md file. However if there is any BREAKING CHANGE, the commit will always appear in the changelog. - -### Revert -If the commit reverts a previous commit, it should begin with `revert:`, followed by the header of the reverted commit. In the body it should say: `This reverts commit `., where the hash is the SHA of the commit being reverted. - -## Scope -The scope could be anything specifying place of the commit change. For example: `router`, `view`, `querybuilder`, `database`, `model` and so on. - -## Subject -The subject contains succinct description of the change: - -- use the imperative, present tense: "change" not "changed" nor "changes". -- don't capitalize first letter -- no dot (.) at the end - -## Body - -Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes". -The body should include the motivation for the change and contrast this with previous behavior. - -## Footer - -The footer should contain any information about **Breaking Changes** and is also the place to -reference GitHub issues that this commit **Closes**. - -**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this. - diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index c119b32..0000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1,38 +0,0 @@ -# Contributing - -We love pull requests. And following this guidelines will make your pull request easier to merge - -## Prerequisites - -- Install [EditorConfig](http://editorconfig.org/) plugin for your code editor to make sure it uses correct settings. -- Fork the repository and clone your fork. -- Install dependencies: `npm install`. - -## Coding style - -We make use of [standard](https://standardjs.com/) to lint our code. Standard does not need a config file and comes with set of non-configurable rules. - -## Development work-flow - -Always make sure to lint and test your code before pushing it to the GitHub. - -```bash -npm test -``` - -Just lint the code - -```bash -npm run lint -``` - -**Make sure you add sufficient tests for the change**. - -## Other notes - -- Do not change version number inside the `package.json` file. -- Do not update `CHANGELOG.md` file. - -## Need help? - -Feel free to ask. diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index b77924c..0000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,23 +0,0 @@ - - -## Prerequisites - -We do our best to reply to all the issues on time. If you will follow the given guidelines, the turn around time will be faster. - -- Ensure the issue isn't already reported. -- Ensure you are reporting the bug in the correct repo. - -*Delete the above section and the instructions in the sections below before submitting* - -## Description - -If this is a feature request, explain why it should be added. Specific use-cases are best. - -For bug reports, please provide as much *relevant* info as possible. - -## Package version - - -## Error Message & Stack Trace - -## Relevant Information diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 5d30a30..0000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,28 +0,0 @@ - - -## Proposed changes - -Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue. - -## Types of changes - -What types of changes does your code introduce? - -_Put an `x` in the boxes that apply_ - -- [ ] Bugfix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - -## Checklist - -_Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code._ - -- [ ] I have read the [CONTRIBUTING](https://github.com/edge-js/edge/blob/master/.github/CONTRIBUTING.md) doc -- [ ] Lint and unit tests pass locally with my changes -- [ ] I have added tests that prove my fix is effective or that my feature works. -- [ ] I have added necessary documentation (if appropriate) - -## Further comments - -If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc... diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml new file mode 100644 index 0000000..8a117cc --- /dev/null +++ b/.github/workflows/checks.yml @@ -0,0 +1,14 @@ +name: checks +on: + - push + - pull_request + +jobs: + test: + uses: edge-js/.github/.github/workflows/test.yml@main + + lint: + uses: edge-js/.github/.github/workflows/lint.yml@main + + typecheck: + uses: edge-js/.github/.github/workflows/typecheck.yml@main diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index d4e0d85..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: test -on: - - push - - pull_request -jobs: - linux: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: - - 14.15.4 - - 19.x - steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - name: Install - run: npm install - - name: Run tests - run: npm test - windows: - runs-on: windows-latest - strategy: - matrix: - node-version: - - 14.15.4 - - 19.x - steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - name: Install - run: npm install - - name: Run tests - run: npm test diff --git a/.husky/commit-msg b/.husky/commit-msg index 4654c12..4002db7 100755 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -1,3 +1,4 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" -HUSKY_GIT_PARAMS=$1 node ./node_modules/@adonisjs/mrm-preset/validate-commit/conventional/validate.js +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npx --no -- commitlint --edit diff --git a/.prettierignore b/.prettierignore index 1f892ec..970a245 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,7 +5,7 @@ config.json package.json *.html fixtures/* -async-fixtures/* -newline-fixtures/* +async_fixtures/* +newline_fixtures/* *.txt *.md diff --git a/async-fixtures/components-advanced-props/compiled.js b/async-fixtures/components-advanced-props/compiled.js deleted file mode 100644 index 714ce2f..0000000 --- a/async-fixtures/components-advanced-props/compiled.js +++ /dev/null @@ -1,9 +0,0 @@ -let out = ""; -let $lineNumber = 1; -let $filename = "{{__dirname}}index.edge"; -try { -out += await template.compileComponent("components-advanced-props/button")(template, template.getComponentState({ class: 'mb-4 px-4', id: 'foo-bar', title: 'Click me' }, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); -} catch (error) { -template.reThrow(error, $filename, $lineNumber); -} -return out; \ No newline at end of file diff --git a/async-fixtures/components-falsy-args/index.edge b/async-fixtures/components-falsy-args/index.edge deleted file mode 100644 index a20e150..0000000 --- a/async-fixtures/components-falsy-args/index.edge +++ /dev/null @@ -1 +0,0 @@ -@!component("components-falsy-args/alert", index = 0) \ No newline at end of file diff --git a/async-fixtures/components-partials/index.edge b/async-fixtures/components-partials/index.edge deleted file mode 100644 index c185bff..0000000 --- a/async-fixtures/components-partials/index.edge +++ /dev/null @@ -1,3 +0,0 @@ -@component("components-partials/alert", username = "virk") - Hello {{ username || "Guest" }} -@endcomponent \ No newline at end of file diff --git a/async-fixtures/layout-allow-set-calls/index.edge b/async-fixtures/layout-allow-set-calls/index.edge deleted file mode 100644 index 6ffeaf0..0000000 --- a/async-fixtures/layout-allow-set-calls/index.edge +++ /dev/null @@ -1,2 +0,0 @@ -@layout("layout-allow-set-calls/master") -@set("username", "virk") \ No newline at end of file diff --git a/async-fixtures/layout-tag-sections/index.edge b/async-fixtures/layout-tag-sections/index.edge deleted file mode 100644 index 0ebd12f..0000000 --- a/async-fixtures/layout-tag-sections/index.edge +++ /dev/null @@ -1 +0,0 @@ -@layout("layout-tag-sections/master") \ No newline at end of file diff --git a/async-fixtures/layout-tag/index.edge b/async-fixtures/layout-tag/index.edge deleted file mode 100644 index 807ff63..0000000 --- a/async-fixtures/layout-tag/index.edge +++ /dev/null @@ -1 +0,0 @@ -@layout("layout-tag/master") \ No newline at end of file diff --git a/async-fixtures/components-advanced-props/button.edge b/async_fixtures/components-advanced-props-compat/button.edge similarity index 100% rename from async-fixtures/components-advanced-props/button.edge rename to async_fixtures/components-advanced-props-compat/button.edge diff --git a/async_fixtures/components-advanced-props-compat/compiled.js b/async_fixtures/components-advanced-props-compat/compiled.js new file mode 100644 index 0000000..8a3fc85 --- /dev/null +++ b/async_fixtures/components-advanced-props-compat/compiled.js @@ -0,0 +1,13 @@ +let out = ""; +let $lineNumber = 1; +let $filename = "{{__dirname}}index.edge"; +try { +out += await template.compileComponent("components-advanced-props-compat/button")(template, template.getComponentState({ + class: 'mb-4 px-4', + id: 'foo-bar', + title: 'Click me' +}, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); +} catch (error) { +template.reThrow(error, $filename, $lineNumber); +} +return out; \ No newline at end of file diff --git a/async_fixtures/components-advanced-props-compat/index.edge b/async_fixtures/components-advanced-props-compat/index.edge new file mode 100644 index 0000000..ffc0f76 --- /dev/null +++ b/async_fixtures/components-advanced-props-compat/index.edge @@ -0,0 +1,5 @@ +@!component("components-advanced-props-compat/button", { + class: 'mb-4 px-4', + id: 'foo-bar', + title: 'Click me' +}) \ No newline at end of file diff --git a/async-fixtures/components-advanced-props/index.json b/async_fixtures/components-advanced-props-compat/index.json similarity index 100% rename from async-fixtures/components-advanced-props/index.json rename to async_fixtures/components-advanced-props-compat/index.json diff --git a/async-fixtures/components-advanced-props/index.txt b/async_fixtures/components-advanced-props-compat/index.txt similarity index 100% rename from async-fixtures/components-advanced-props/index.txt rename to async_fixtures/components-advanced-props-compat/index.txt diff --git a/async_fixtures/components-advanced-props/button.edge b/async_fixtures/components-advanced-props/button.edge new file mode 100644 index 0000000..fa6fed0 --- /dev/null +++ b/async_fixtures/components-advanced-props/button.edge @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/async_fixtures/components-advanced-props/compiled.js b/async_fixtures/components-advanced-props/compiled.js new file mode 100644 index 0000000..e6b438e --- /dev/null +++ b/async_fixtures/components-advanced-props/compiled.js @@ -0,0 +1,13 @@ +let out = ""; +let $lineNumber = 1; +let $filename = "{{__dirname}}index.edge"; +try { +out += await template.compileComponent("components-advanced-props/button")(template, template.getComponentState({ + class: 'mb-4 px-4', + id: 'foo-bar', + title: 'Click me' +}, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); +} catch (error) { +template.reThrow(error, $filename, $lineNumber); +} +return out; \ No newline at end of file diff --git a/async-fixtures/components-advanced-props/index.edge b/async_fixtures/components-advanced-props/index.edge similarity index 100% rename from async-fixtures/components-advanced-props/index.edge rename to async_fixtures/components-advanced-props/index.edge diff --git a/async-fixtures/each-tag-array/index.json b/async_fixtures/components-advanced-props/index.json similarity index 100% rename from async-fixtures/each-tag-array/index.json rename to async_fixtures/components-advanced-props/index.json diff --git a/async_fixtures/components-advanced-props/index.txt b/async_fixtures/components-advanced-props/index.txt new file mode 100644 index 0000000..97fb58a --- /dev/null +++ b/async_fixtures/components-advanced-props/index.txt @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/async-fixtures/components-falsy-args/alert.edge b/async_fixtures/components-falsy-args/alert.edge similarity index 100% rename from async-fixtures/components-falsy-args/alert.edge rename to async_fixtures/components-falsy-args/alert.edge diff --git a/async-fixtures/components-falsy-args/compiled.js b/async_fixtures/components-falsy-args/compiled.js similarity index 53% rename from async-fixtures/components-falsy-args/compiled.js rename to async_fixtures/components-falsy-args/compiled.js index 88de088..4186d87 100644 --- a/async-fixtures/components-falsy-args/compiled.js +++ b/async_fixtures/components-falsy-args/compiled.js @@ -2,7 +2,9 @@ let out = ""; let $lineNumber = 1; let $filename = "{{__dirname}}index.edge"; try { -out += await template.compileComponent("components-falsy-args/alert")(template, template.getComponentState({ index: 0 }, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); +out += await template.compileComponent("components-falsy-args/alert")(template, template.getComponentState({ + index: 0 +}, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); } catch (error) { template.reThrow(error, $filename, $lineNumber); } diff --git a/async_fixtures/components-falsy-args/index.edge b/async_fixtures/components-falsy-args/index.edge new file mode 100644 index 0000000..93dbe7d --- /dev/null +++ b/async_fixtures/components-falsy-args/index.edge @@ -0,0 +1 @@ +@!component("components-falsy-args/alert", { index: 0 }) \ No newline at end of file diff --git a/async-fixtures/components-falsy-args/index.json b/async_fixtures/components-falsy-args/index.json similarity index 100% rename from async-fixtures/components-falsy-args/index.json rename to async_fixtures/components-falsy-args/index.json diff --git a/async-fixtures/components-falsy-args/index.txt b/async_fixtures/components-falsy-args/index.txt similarity index 100% rename from async-fixtures/components-falsy-args/index.txt rename to async_fixtures/components-falsy-args/index.txt diff --git a/async-fixtures/components-isolated-state/alert.edge b/async_fixtures/components-isolated-state/alert.edge similarity index 100% rename from async-fixtures/components-isolated-state/alert.edge rename to async_fixtures/components-isolated-state/alert.edge diff --git a/async-fixtures/components-isolated-state/compiled.js b/async_fixtures/components-isolated-state/compiled.js similarity index 100% rename from async-fixtures/components-isolated-state/compiled.js rename to async_fixtures/components-isolated-state/compiled.js diff --git a/async-fixtures/components-isolated-state/index.edge b/async_fixtures/components-isolated-state/index.edge similarity index 100% rename from async-fixtures/components-isolated-state/index.edge rename to async_fixtures/components-isolated-state/index.edge diff --git a/async-fixtures/components-isolated-state/index.json b/async_fixtures/components-isolated-state/index.json similarity index 100% rename from async-fixtures/components-isolated-state/index.json rename to async_fixtures/components-isolated-state/index.json diff --git a/async-fixtures/components-isolated-state/index.txt b/async_fixtures/components-isolated-state/index.txt similarity index 100% rename from async-fixtures/components-isolated-state/index.txt rename to async_fixtures/components-isolated-state/index.txt diff --git a/async-fixtures/components-named-slots/alert.edge b/async_fixtures/components-named-slots/alert.edge similarity index 100% rename from async-fixtures/components-named-slots/alert.edge rename to async_fixtures/components-named-slots/alert.edge diff --git a/async-fixtures/components-named-slots/compiled.js b/async_fixtures/components-named-slots/compiled.js similarity index 100% rename from async-fixtures/components-named-slots/compiled.js rename to async_fixtures/components-named-slots/compiled.js diff --git a/async-fixtures/components-named-slots/index.edge b/async_fixtures/components-named-slots/index.edge similarity index 100% rename from async-fixtures/components-named-slots/index.edge rename to async_fixtures/components-named-slots/index.edge diff --git a/async-fixtures/components-named-slots/index.json b/async_fixtures/components-named-slots/index.json similarity index 100% rename from async-fixtures/components-named-slots/index.json rename to async_fixtures/components-named-slots/index.json diff --git a/async-fixtures/components-named-slots/index.txt b/async_fixtures/components-named-slots/index.txt similarity index 100% rename from async-fixtures/components-named-slots/index.txt rename to async_fixtures/components-named-slots/index.txt diff --git a/async-fixtures/components-partials/alert.edge b/async_fixtures/components-partials/alert.edge similarity index 100% rename from async-fixtures/components-partials/alert.edge rename to async_fixtures/components-partials/alert.edge diff --git a/async-fixtures/components-partials/compiled.js b/async_fixtures/components-partials/compiled.js similarity index 75% rename from async-fixtures/components-partials/compiled.js rename to async_fixtures/components-partials/compiled.js index db401d5..416846a 100644 --- a/async-fixtures/components-partials/compiled.js +++ b/async_fixtures/components-partials/compiled.js @@ -2,7 +2,9 @@ let out = ""; let $lineNumber = 1; let $filename = "{{__dirname}}index.edge"; try { -out += await template.compileComponent("components-partials/alert")(template, template.getComponentState({ username: "virk" }, { $context: Object.assign({}, $context), main: async function () { const $context = this.$context; +out += await template.compileComponent("components-partials/alert")(template, template.getComponentState({ + username: 'virk' +}, { $context: Object.assign({}, $context), main: async function () { const $context = this.$context; let slot_main = ""; try { slot_main += " Hello "; diff --git a/async_fixtures/components-partials/index.edge b/async_fixtures/components-partials/index.edge new file mode 100644 index 0000000..6e11f8b --- /dev/null +++ b/async_fixtures/components-partials/index.edge @@ -0,0 +1,3 @@ +@component("components-partials/alert", { username: 'virk' }) + Hello {{ username || "Guest" }} +@endcomponent \ No newline at end of file diff --git a/async-fixtures/components-partials/index.json b/async_fixtures/components-partials/index.json similarity index 100% rename from async-fixtures/components-partials/index.json rename to async_fixtures/components-partials/index.json diff --git a/async-fixtures/components-partials/index.txt b/async_fixtures/components-partials/index.txt similarity index 100% rename from async-fixtures/components-partials/index.txt rename to async_fixtures/components-partials/index.txt diff --git a/async-fixtures/components-partials/partial.edge b/async_fixtures/components-partials/partial.edge similarity index 100% rename from async-fixtures/components-partials/partial.edge rename to async_fixtures/components-partials/partial.edge diff --git a/async-fixtures/components-props-as-reference/alert.edge b/async_fixtures/components-props-as-reference/alert.edge similarity index 100% rename from async-fixtures/components-props-as-reference/alert.edge rename to async_fixtures/components-props-as-reference/alert.edge diff --git a/async-fixtures/components-props-as-reference/compiled.js b/async_fixtures/components-props-as-reference/compiled.js similarity index 100% rename from async-fixtures/components-props-as-reference/compiled.js rename to async_fixtures/components-props-as-reference/compiled.js diff --git a/async-fixtures/components-props-as-reference/index.edge b/async_fixtures/components-props-as-reference/index.edge similarity index 100% rename from async-fixtures/components-props-as-reference/index.edge rename to async_fixtures/components-props-as-reference/index.edge diff --git a/async-fixtures/components-props-as-reference/index.json b/async_fixtures/components-props-as-reference/index.json similarity index 100% rename from async-fixtures/components-props-as-reference/index.json rename to async_fixtures/components-props-as-reference/index.json diff --git a/async-fixtures/components-props-as-reference/index.txt b/async_fixtures/components-props-as-reference/index.txt similarity index 100% rename from async-fixtures/components-props-as-reference/index.txt rename to async_fixtures/components-props-as-reference/index.txt diff --git a/async-fixtures/components-props/alert.edge b/async_fixtures/components-props/alert.edge similarity index 100% rename from async-fixtures/components-props/alert.edge rename to async_fixtures/components-props/alert.edge diff --git a/async-fixtures/components-props/compiled.js b/async_fixtures/components-props/compiled.js similarity index 73% rename from async-fixtures/components-props/compiled.js rename to async_fixtures/components-props/compiled.js index 4e796f4..efbbb7a 100644 --- a/async-fixtures/components-props/compiled.js +++ b/async_fixtures/components-props/compiled.js @@ -2,7 +2,9 @@ let out = ""; let $lineNumber = 1; let $filename = "{{__dirname}}index.edge"; try { -out += await template.compileComponent("components-props/alert")(template, template.getComponentState({ "title": "H1" }, { $context: Object.assign({}, $context), main: async function () { const $context = this.$context; +out += await template.compileComponent("components-props/alert")(template, template.getComponentState({ + "title": "H1" +}, { $context: Object.assign({}, $context), main: async function () { const $context = this.$context; let slot_main = ""; try { slot_main += "Hello world"; diff --git a/async-fixtures/components-props/index.edge b/async_fixtures/components-props/index.edge similarity index 100% rename from async-fixtures/components-props/index.edge rename to async_fixtures/components-props/index.edge diff --git a/async-fixtures/components-props/index.json b/async_fixtures/components-props/index.json similarity index 100% rename from async-fixtures/components-props/index.json rename to async_fixtures/components-props/index.json diff --git a/async-fixtures/components-props/index.txt b/async_fixtures/components-props/index.txt similarity index 100% rename from async-fixtures/components-props/index.txt rename to async_fixtures/components-props/index.txt diff --git a/async-fixtures/components-slot-props/alert.edge b/async_fixtures/components-slot-props/alert.edge similarity index 100% rename from async-fixtures/components-slot-props/alert.edge rename to async_fixtures/components-slot-props/alert.edge diff --git a/async-fixtures/components-slot-props/compiled.js b/async_fixtures/components-slot-props/compiled.js similarity index 100% rename from async-fixtures/components-slot-props/compiled.js rename to async_fixtures/components-slot-props/compiled.js diff --git a/async-fixtures/components-slot-props/index.edge b/async_fixtures/components-slot-props/index.edge similarity index 100% rename from async-fixtures/components-slot-props/index.edge rename to async_fixtures/components-slot-props/index.edge diff --git a/async-fixtures/components-slot-props/index.json b/async_fixtures/components-slot-props/index.json similarity index 100% rename from async-fixtures/components-slot-props/index.json rename to async_fixtures/components-slot-props/index.json diff --git a/async-fixtures/components-slot-props/index.txt b/async_fixtures/components-slot-props/index.txt similarity index 100% rename from async-fixtures/components-slot-props/index.txt rename to async_fixtures/components-slot-props/index.txt diff --git a/async-fixtures/components-slots-partials/alert.edge b/async_fixtures/components-slots-partials/alert.edge similarity index 100% rename from async-fixtures/components-slots-partials/alert.edge rename to async_fixtures/components-slots-partials/alert.edge diff --git a/async-fixtures/components-slots-partials/compiled.js b/async_fixtures/components-slots-partials/compiled.js similarity index 100% rename from async-fixtures/components-slots-partials/compiled.js rename to async_fixtures/components-slots-partials/compiled.js diff --git a/async-fixtures/components-slots-partials/index.edge b/async_fixtures/components-slots-partials/index.edge similarity index 100% rename from async-fixtures/components-slots-partials/index.edge rename to async_fixtures/components-slots-partials/index.edge diff --git a/async-fixtures/components-slots-partials/index.json b/async_fixtures/components-slots-partials/index.json similarity index 100% rename from async-fixtures/components-slots-partials/index.json rename to async_fixtures/components-slots-partials/index.json diff --git a/async-fixtures/components-slots-partials/index.txt b/async_fixtures/components-slots-partials/index.txt similarity index 100% rename from async-fixtures/components-slots-partials/index.txt rename to async_fixtures/components-slots-partials/index.txt diff --git a/async-fixtures/components-slots-partials/partial.edge b/async_fixtures/components-slots-partials/partial.edge similarity index 100% rename from async-fixtures/components-slots-partials/partial.edge rename to async_fixtures/components-slots-partials/partial.edge diff --git a/async-fixtures/components-spread-and-literal/alert.edge b/async_fixtures/components-spread-and-literal/alert.edge similarity index 100% rename from async-fixtures/components-spread-and-literal/alert.edge rename to async_fixtures/components-spread-and-literal/alert.edge diff --git a/async-fixtures/components-spread-and-literal/compiled.js b/async_fixtures/components-spread-and-literal/compiled.js similarity index 50% rename from async-fixtures/components-spread-and-literal/compiled.js rename to async_fixtures/components-spread-and-literal/compiled.js index bcfa95a..d093bb0 100644 --- a/async-fixtures/components-spread-and-literal/compiled.js +++ b/async_fixtures/components-spread-and-literal/compiled.js @@ -2,7 +2,10 @@ let out = ""; let $lineNumber = 1; let $filename = "{{__dirname}}index.edge"; try { -out += await template.compileComponent("components-spread-and-literal/alert")(template, template.getComponentState({ ...state.data, name: 'virk' }, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); +out += await template.compileComponent("components-spread-and-literal/alert")(template, template.getComponentState({ + ...state.data, + name: 'virk' +}, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); } catch (error) { template.reThrow(error, $filename, $lineNumber); } diff --git a/async-fixtures/components-spread-and-literal/index.edge b/async_fixtures/components-spread-and-literal/index.edge similarity index 100% rename from async-fixtures/components-spread-and-literal/index.edge rename to async_fixtures/components-spread-and-literal/index.edge diff --git a/async-fixtures/components-spread-and-literal/index.json b/async_fixtures/components-spread-and-literal/index.json similarity index 100% rename from async-fixtures/components-spread-and-literal/index.json rename to async_fixtures/components-spread-and-literal/index.json diff --git a/async-fixtures/components-spread-and-literal/index.txt b/async_fixtures/components-spread-and-literal/index.txt similarity index 100% rename from async-fixtures/components-spread-and-literal/index.txt rename to async_fixtures/components-spread-and-literal/index.txt diff --git a/async-fixtures/components-spread/alert.edge b/async_fixtures/components-spread/alert.edge similarity index 100% rename from async-fixtures/components-spread/alert.edge rename to async_fixtures/components-spread/alert.edge diff --git a/async-fixtures/components-spread/compiled.js b/async_fixtures/components-spread/compiled.js similarity index 53% rename from async-fixtures/components-spread/compiled.js rename to async_fixtures/components-spread/compiled.js index c2d54ee..030811d 100644 --- a/async-fixtures/components-spread/compiled.js +++ b/async_fixtures/components-spread/compiled.js @@ -2,7 +2,9 @@ let out = ""; let $lineNumber = 1; let $filename = "{{__dirname}}index.edge"; try { -out += await template.compileComponent("components-spread/alert")(template, template.getComponentState({ ...state.data }, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); +out += await template.compileComponent("components-spread/alert")(template, template.getComponentState({ + ...state.data +}, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); } catch (error) { template.reThrow(error, $filename, $lineNumber); } diff --git a/async-fixtures/components-spread/index.edge b/async_fixtures/components-spread/index.edge similarity index 100% rename from async-fixtures/components-spread/index.edge rename to async_fixtures/components-spread/index.edge diff --git a/async-fixtures/components-spread/index.json b/async_fixtures/components-spread/index.json similarity index 100% rename from async-fixtures/components-spread/index.json rename to async_fixtures/components-spread/index.json diff --git a/async-fixtures/components-spread/index.txt b/async_fixtures/components-spread/index.txt similarity index 100% rename from async-fixtures/components-spread/index.txt rename to async_fixtures/components-spread/index.txt diff --git a/async-fixtures/components-state/alert.edge b/async_fixtures/components-state/alert.edge similarity index 100% rename from async-fixtures/components-state/alert.edge rename to async_fixtures/components-state/alert.edge diff --git a/async-fixtures/components-state/compiled.js b/async_fixtures/components-state/compiled.js similarity index 100% rename from async-fixtures/components-state/compiled.js rename to async_fixtures/components-state/compiled.js diff --git a/async-fixtures/components-state/index.edge b/async_fixtures/components-state/index.edge similarity index 100% rename from async-fixtures/components-state/index.edge rename to async_fixtures/components-state/index.edge diff --git a/async-fixtures/components-state/index.json b/async_fixtures/components-state/index.json similarity index 100% rename from async-fixtures/components-state/index.json rename to async_fixtures/components-state/index.json diff --git a/async-fixtures/components-state/index.txt b/async_fixtures/components-state/index.txt similarity index 100% rename from async-fixtures/components-state/index.txt rename to async_fixtures/components-state/index.txt diff --git a/async-fixtures/components/alert.edge b/async_fixtures/components/alert.edge similarity index 100% rename from async-fixtures/components/alert.edge rename to async_fixtures/components/alert.edge diff --git a/async-fixtures/components/compiled.js b/async_fixtures/components/compiled.js similarity index 100% rename from async-fixtures/components/compiled.js rename to async_fixtures/components/compiled.js diff --git a/async-fixtures/components/index.edge b/async_fixtures/components/index.edge similarity index 100% rename from async-fixtures/components/index.edge rename to async_fixtures/components/index.edge diff --git a/async-fixtures/components/index.json b/async_fixtures/components/index.json similarity index 100% rename from async-fixtures/components/index.json rename to async_fixtures/components/index.json diff --git a/async-fixtures/components/index.txt b/async_fixtures/components/index.txt similarity index 100% rename from async-fixtures/components/index.txt rename to async_fixtures/components/index.txt diff --git a/async-fixtures/debugger/compiled.js b/async_fixtures/debugger/compiled.js similarity index 100% rename from async-fixtures/debugger/compiled.js rename to async_fixtures/debugger/compiled.js diff --git a/async-fixtures/debugger/index.edge b/async_fixtures/debugger/index.edge similarity index 100% rename from async-fixtures/debugger/index.edge rename to async_fixtures/debugger/index.edge diff --git a/async-fixtures/debugger/index.json b/async_fixtures/debugger/index.json similarity index 100% rename from async-fixtures/debugger/index.json rename to async_fixtures/debugger/index.json diff --git a/async-fixtures/debugger/index.txt b/async_fixtures/debugger/index.txt similarity index 100% rename from async-fixtures/debugger/index.txt rename to async_fixtures/debugger/index.txt diff --git a/async-fixtures/each-loop-set/compiled.js b/async_fixtures/each-loop-set-compat/compiled.js similarity index 100% rename from async-fixtures/each-loop-set/compiled.js rename to async_fixtures/each-loop-set-compat/compiled.js diff --git a/async-fixtures/each-loop-set/index.edge b/async_fixtures/each-loop-set-compat/index.edge similarity index 100% rename from async-fixtures/each-loop-set/index.edge rename to async_fixtures/each-loop-set-compat/index.edge diff --git a/async-fixtures/each-loop-set/index.json b/async_fixtures/each-loop-set-compat/index.json similarity index 100% rename from async-fixtures/each-loop-set/index.json rename to async_fixtures/each-loop-set-compat/index.json diff --git a/async-fixtures/each-loop-set/index.txt b/async_fixtures/each-loop-set-compat/index.txt similarity index 100% rename from async-fixtures/each-loop-set/index.txt rename to async_fixtures/each-loop-set-compat/index.txt diff --git a/async-fixtures/each-tag-array/compiled.js b/async_fixtures/each-tag-array/compiled.js similarity index 100% rename from async-fixtures/each-tag-array/compiled.js rename to async_fixtures/each-tag-array/compiled.js diff --git a/async-fixtures/each-tag-array/index.edge b/async_fixtures/each-tag-array/index.edge similarity index 100% rename from async-fixtures/each-tag-array/index.edge rename to async_fixtures/each-tag-array/index.edge diff --git a/async-fixtures/inject-function-calls/index.json b/async_fixtures/each-tag-array/index.json similarity index 100% rename from async-fixtures/inject-function-calls/index.json rename to async_fixtures/each-tag-array/index.json diff --git a/async-fixtures/each-tag-array/index.txt b/async_fixtures/each-tag-array/index.txt similarity index 100% rename from async-fixtures/each-tag-array/index.txt rename to async_fixtures/each-tag-array/index.txt diff --git a/async-fixtures/each-tag-else/compiled.js b/async_fixtures/each-tag-else/compiled.js similarity index 100% rename from async-fixtures/each-tag-else/compiled.js rename to async_fixtures/each-tag-else/compiled.js diff --git a/async-fixtures/each-tag-else/index.edge b/async_fixtures/each-tag-else/index.edge similarity index 100% rename from async-fixtures/each-tag-else/index.edge rename to async_fixtures/each-tag-else/index.edge diff --git a/async-fixtures/each-tag-else/index.json b/async_fixtures/each-tag-else/index.json similarity index 100% rename from async-fixtures/each-tag-else/index.json rename to async_fixtures/each-tag-else/index.json diff --git a/async-fixtures/each-tag-else/index.txt b/async_fixtures/each-tag-else/index.txt similarity index 100% rename from async-fixtures/each-tag-else/index.txt rename to async_fixtures/each-tag-else/index.txt diff --git a/async-fixtures/each-tag-include/compiled.js b/async_fixtures/each-tag-include/compiled.js similarity index 100% rename from async-fixtures/each-tag-include/compiled.js rename to async_fixtures/each-tag-include/compiled.js diff --git a/async-fixtures/each-tag-include/index.edge b/async_fixtures/each-tag-include/index.edge similarity index 100% rename from async-fixtures/each-tag-include/index.edge rename to async_fixtures/each-tag-include/index.edge diff --git a/async-fixtures/each-tag-include/index.json b/async_fixtures/each-tag-include/index.json similarity index 100% rename from async-fixtures/each-tag-include/index.json rename to async_fixtures/each-tag-include/index.json diff --git a/async-fixtures/each-tag-include/index.txt b/async_fixtures/each-tag-include/index.txt similarity index 100% rename from async-fixtures/each-tag-include/index.txt rename to async_fixtures/each-tag-include/index.txt diff --git a/async-fixtures/each-tag-include/user.edge b/async_fixtures/each-tag-include/user.edge similarity index 100% rename from async-fixtures/each-tag-include/user.edge rename to async_fixtures/each-tag-include/user.edge diff --git a/async-fixtures/each-tag-index/compiled.js b/async_fixtures/each-tag-index/compiled.js similarity index 100% rename from async-fixtures/each-tag-index/compiled.js rename to async_fixtures/each-tag-index/compiled.js diff --git a/async-fixtures/each-tag-index/index.edge b/async_fixtures/each-tag-index/index.edge similarity index 100% rename from async-fixtures/each-tag-index/index.edge rename to async_fixtures/each-tag-index/index.edge diff --git a/async-fixtures/each-tag-index/index.json b/async_fixtures/each-tag-index/index.json similarity index 100% rename from async-fixtures/each-tag-index/index.json rename to async_fixtures/each-tag-index/index.json diff --git a/async-fixtures/each-tag-index/index.txt b/async_fixtures/each-tag-index/index.txt similarity index 100% rename from async-fixtures/each-tag-index/index.txt rename to async_fixtures/each-tag-index/index.txt diff --git a/async-fixtures/each-tag/compiled.js b/async_fixtures/each-tag/compiled.js similarity index 100% rename from async-fixtures/each-tag/compiled.js rename to async_fixtures/each-tag/compiled.js diff --git a/async-fixtures/each-tag/index.edge b/async_fixtures/each-tag/index.edge similarity index 100% rename from async-fixtures/each-tag/index.edge rename to async_fixtures/each-tag/index.edge diff --git a/async-fixtures/each-tag/index.json b/async_fixtures/each-tag/index.json similarity index 100% rename from async-fixtures/each-tag/index.json rename to async_fixtures/each-tag/index.json diff --git a/async-fixtures/each-tag/index.txt b/async_fixtures/each-tag/index.txt similarity index 100% rename from async-fixtures/each-tag/index.txt rename to async_fixtures/each-tag/index.txt diff --git a/async-fixtures/else-if-tag/compiled.js b/async_fixtures/else-if-tag/compiled.js similarity index 100% rename from async-fixtures/else-if-tag/compiled.js rename to async_fixtures/else-if-tag/compiled.js diff --git a/async-fixtures/else-if-tag/index.edge b/async_fixtures/else-if-tag/index.edge similarity index 100% rename from async-fixtures/else-if-tag/index.edge rename to async_fixtures/else-if-tag/index.edge diff --git a/async-fixtures/else-if-tag/index.json b/async_fixtures/else-if-tag/index.json similarity index 100% rename from async-fixtures/else-if-tag/index.json rename to async_fixtures/else-if-tag/index.json diff --git a/async-fixtures/else-if-tag/index.txt b/async_fixtures/else-if-tag/index.txt similarity index 100% rename from async-fixtures/else-if-tag/index.txt rename to async_fixtures/else-if-tag/index.txt diff --git a/async-fixtures/else-tag/compiled.js b/async_fixtures/else-tag/compiled.js similarity index 100% rename from async-fixtures/else-tag/compiled.js rename to async_fixtures/else-tag/compiled.js diff --git a/async-fixtures/else-tag/index.edge b/async_fixtures/else-tag/index.edge similarity index 100% rename from async-fixtures/else-tag/index.edge rename to async_fixtures/else-tag/index.edge diff --git a/async-fixtures/else-tag/index.json b/async_fixtures/else-tag/index.json similarity index 100% rename from async-fixtures/else-tag/index.json rename to async_fixtures/else-tag/index.json diff --git a/async-fixtures/else-tag/index.txt b/async_fixtures/else-tag/index.txt similarity index 100% rename from async-fixtures/else-tag/index.txt rename to async_fixtures/else-tag/index.txt diff --git a/async-fixtures/if-tag/compiled.js b/async_fixtures/if-tag/compiled.js similarity index 100% rename from async-fixtures/if-tag/compiled.js rename to async_fixtures/if-tag/compiled.js diff --git a/async-fixtures/if-tag/index.edge b/async_fixtures/if-tag/index.edge similarity index 100% rename from async-fixtures/if-tag/index.edge rename to async_fixtures/if-tag/index.edge diff --git a/async-fixtures/if-tag/index.json b/async_fixtures/if-tag/index.json similarity index 100% rename from async-fixtures/if-tag/index.json rename to async_fixtures/if-tag/index.json diff --git a/async-fixtures/if-tag/index.txt b/async_fixtures/if-tag/index.txt similarity index 100% rename from async-fixtures/if-tag/index.txt rename to async_fixtures/if-tag/index.txt diff --git a/async-fixtures/include-conditionals/compiled.js b/async_fixtures/include-conditionals/compiled.js similarity index 100% rename from async-fixtures/include-conditionals/compiled.js rename to async_fixtures/include-conditionals/compiled.js diff --git a/async-fixtures/include-conditionals/index.edge b/async_fixtures/include-conditionals/index.edge similarity index 100% rename from async-fixtures/include-conditionals/index.edge rename to async_fixtures/include-conditionals/index.edge diff --git a/async-fixtures/include-conditionals/index.json b/async_fixtures/include-conditionals/index.json similarity index 100% rename from async-fixtures/include-conditionals/index.json rename to async_fixtures/include-conditionals/index.json diff --git a/async-fixtures/include-conditionals/index.txt b/async_fixtures/include-conditionals/index.txt similarity index 100% rename from async-fixtures/include-conditionals/index.txt rename to async_fixtures/include-conditionals/index.txt diff --git a/async-fixtures/include-conditionals/virk.edge b/async_fixtures/include-conditionals/virk.edge similarity index 100% rename from async-fixtures/include-conditionals/virk.edge rename to async_fixtures/include-conditionals/virk.edge diff --git a/async-fixtures/include-identifier/compiled.js b/async_fixtures/include-identifier/compiled.js similarity index 100% rename from async-fixtures/include-identifier/compiled.js rename to async_fixtures/include-identifier/compiled.js diff --git a/async-fixtures/include-identifier/index.edge b/async_fixtures/include-identifier/index.edge similarity index 100% rename from async-fixtures/include-identifier/index.edge rename to async_fixtures/include-identifier/index.edge diff --git a/async-fixtures/include-identifier/index.json b/async_fixtures/include-identifier/index.json similarity index 100% rename from async-fixtures/include-identifier/index.json rename to async_fixtures/include-identifier/index.json diff --git a/async-fixtures/include-identifier/index.txt b/async_fixtures/include-identifier/index.txt similarity index 100% rename from async-fixtures/include-identifier/index.txt rename to async_fixtures/include-identifier/index.txt diff --git a/async-fixtures/include-identifier/partial.edge b/async_fixtures/include-identifier/partial.edge similarity index 100% rename from async-fixtures/include-identifier/partial.edge rename to async_fixtures/include-identifier/partial.edge diff --git a/async-fixtures/include-if-identifier/compiled.js b/async_fixtures/include-if-identifier/compiled.js similarity index 100% rename from async-fixtures/include-if-identifier/compiled.js rename to async_fixtures/include-if-identifier/compiled.js diff --git a/async-fixtures/include-if-identifier/index.edge b/async_fixtures/include-if-identifier/index.edge similarity index 100% rename from async-fixtures/include-if-identifier/index.edge rename to async_fixtures/include-if-identifier/index.edge diff --git a/async-fixtures/include-if-identifier/index.json b/async_fixtures/include-if-identifier/index.json similarity index 100% rename from async-fixtures/include-if-identifier/index.json rename to async_fixtures/include-if-identifier/index.json diff --git a/async-fixtures/include-if-identifier/index.txt b/async_fixtures/include-if-identifier/index.txt similarity index 100% rename from async-fixtures/include-if-identifier/index.txt rename to async_fixtures/include-if-identifier/index.txt diff --git a/async-fixtures/include-if-identifier/partial.edge b/async_fixtures/include-if-identifier/partial.edge similarity index 100% rename from async-fixtures/include-if-identifier/partial.edge rename to async_fixtures/include-if-identifier/partial.edge diff --git a/async-fixtures/include-if-literal/compiled.js b/async_fixtures/include-if-literal/compiled.js similarity index 100% rename from async-fixtures/include-if-literal/compiled.js rename to async_fixtures/include-if-literal/compiled.js diff --git a/async-fixtures/include-if-literal/index.edge b/async_fixtures/include-if-literal/index.edge similarity index 100% rename from async-fixtures/include-if-literal/index.edge rename to async_fixtures/include-if-literal/index.edge diff --git a/async-fixtures/include-if-literal/index.json b/async_fixtures/include-if-literal/index.json similarity index 100% rename from async-fixtures/include-if-literal/index.json rename to async_fixtures/include-if-literal/index.json diff --git a/async-fixtures/include-if-literal/index.txt b/async_fixtures/include-if-literal/index.txt similarity index 100% rename from async-fixtures/include-if-literal/index.txt rename to async_fixtures/include-if-literal/index.txt diff --git a/async-fixtures/include-if-literal/partial.edge b/async_fixtures/include-if-literal/partial.edge similarity index 100% rename from async-fixtures/include-if-literal/partial.edge rename to async_fixtures/include-if-literal/partial.edge diff --git a/async-fixtures/include-literal/compiled.js b/async_fixtures/include-literal/compiled.js similarity index 100% rename from async-fixtures/include-literal/compiled.js rename to async_fixtures/include-literal/compiled.js diff --git a/async-fixtures/include-literal/index.edge b/async_fixtures/include-literal/index.edge similarity index 100% rename from async-fixtures/include-literal/index.edge rename to async_fixtures/include-literal/index.edge diff --git a/async-fixtures/include-literal/index.json b/async_fixtures/include-literal/index.json similarity index 100% rename from async-fixtures/include-literal/index.json rename to async_fixtures/include-literal/index.json diff --git a/async-fixtures/include-literal/index.txt b/async_fixtures/include-literal/index.txt similarity index 100% rename from async-fixtures/include-literal/index.txt rename to async_fixtures/include-literal/index.txt diff --git a/async-fixtures/include-literal/partial.edge b/async_fixtures/include-literal/partial.edge similarity index 100% rename from async-fixtures/include-literal/partial.edge rename to async_fixtures/include-literal/partial.edge diff --git a/async-fixtures/include-nested-shared-locals/compiled.js b/async_fixtures/include-nested-shared-locals/compiled.js similarity index 100% rename from async-fixtures/include-nested-shared-locals/compiled.js rename to async_fixtures/include-nested-shared-locals/compiled.js diff --git a/async-fixtures/include-nested-shared-locals/index.edge b/async_fixtures/include-nested-shared-locals/index.edge similarity index 100% rename from async-fixtures/include-nested-shared-locals/index.edge rename to async_fixtures/include-nested-shared-locals/index.edge diff --git a/async-fixtures/include-nested-shared-locals/index.json b/async_fixtures/include-nested-shared-locals/index.json similarity index 100% rename from async-fixtures/include-nested-shared-locals/index.json rename to async_fixtures/include-nested-shared-locals/index.json diff --git a/async-fixtures/include-nested-shared-locals/index.txt b/async_fixtures/include-nested-shared-locals/index.txt similarity index 100% rename from async-fixtures/include-nested-shared-locals/index.txt rename to async_fixtures/include-nested-shared-locals/index.txt diff --git a/async-fixtures/include-nested-shared-locals/partial-1.edge b/async_fixtures/include-nested-shared-locals/partial-1.edge similarity index 100% rename from async-fixtures/include-nested-shared-locals/partial-1.edge rename to async_fixtures/include-nested-shared-locals/partial-1.edge diff --git a/async-fixtures/include-nested-shared-locals/partial.edge b/async_fixtures/include-nested-shared-locals/partial.edge similarity index 100% rename from async-fixtures/include-nested-shared-locals/partial.edge rename to async_fixtures/include-nested-shared-locals/partial.edge diff --git a/async-fixtures/include-nested/compiled.js b/async_fixtures/include-nested/compiled.js similarity index 100% rename from async-fixtures/include-nested/compiled.js rename to async_fixtures/include-nested/compiled.js diff --git a/async-fixtures/include-nested/index.edge b/async_fixtures/include-nested/index.edge similarity index 100% rename from async-fixtures/include-nested/index.edge rename to async_fixtures/include-nested/index.edge diff --git a/async-fixtures/include-nested/index.json b/async_fixtures/include-nested/index.json similarity index 100% rename from async-fixtures/include-nested/index.json rename to async_fixtures/include-nested/index.json diff --git a/async-fixtures/include-nested/index.txt b/async_fixtures/include-nested/index.txt similarity index 100% rename from async-fixtures/include-nested/index.txt rename to async_fixtures/include-nested/index.txt diff --git a/async-fixtures/include-nested/partial-1.edge b/async_fixtures/include-nested/partial-1.edge similarity index 100% rename from async-fixtures/include-nested/partial-1.edge rename to async_fixtures/include-nested/partial-1.edge diff --git a/async-fixtures/include-nested/partial.edge b/async_fixtures/include-nested/partial.edge similarity index 100% rename from async-fixtures/include-nested/partial.edge rename to async_fixtures/include-nested/partial.edge diff --git a/async-fixtures/include-shared-ctx/compiled.js b/async_fixtures/include-shared-ctx/compiled.js similarity index 100% rename from async-fixtures/include-shared-ctx/compiled.js rename to async_fixtures/include-shared-ctx/compiled.js diff --git a/async-fixtures/include-shared-ctx/index.edge b/async_fixtures/include-shared-ctx/index.edge similarity index 100% rename from async-fixtures/include-shared-ctx/index.edge rename to async_fixtures/include-shared-ctx/index.edge diff --git a/async-fixtures/include-shared-ctx/index.json b/async_fixtures/include-shared-ctx/index.json similarity index 100% rename from async-fixtures/include-shared-ctx/index.json rename to async_fixtures/include-shared-ctx/index.json diff --git a/async-fixtures/include-shared-ctx/index.txt b/async_fixtures/include-shared-ctx/index.txt similarity index 100% rename from async-fixtures/include-shared-ctx/index.txt rename to async_fixtures/include-shared-ctx/index.txt diff --git a/async-fixtures/include-shared-ctx/partial.edge b/async_fixtures/include-shared-ctx/partial.edge similarity index 100% rename from async-fixtures/include-shared-ctx/partial.edge rename to async_fixtures/include-shared-ctx/partial.edge diff --git a/async-fixtures/inject-function-calls/compiled.js b/async_fixtures/inject-function-calls/compiled.js similarity index 100% rename from async-fixtures/inject-function-calls/compiled.js rename to async_fixtures/inject-function-calls/compiled.js diff --git a/async-fixtures/inject-function-calls/index.edge b/async_fixtures/inject-function-calls/index.edge similarity index 100% rename from async-fixtures/inject-function-calls/index.edge rename to async_fixtures/inject-function-calls/index.edge diff --git a/async-fixtures/inject-identifier/index.json b/async_fixtures/inject-function-calls/index.json similarity index 100% rename from async-fixtures/inject-identifier/index.json rename to async_fixtures/inject-function-calls/index.json diff --git a/async-fixtures/inject-function-calls/index.txt b/async_fixtures/inject-function-calls/index.txt similarity index 100% rename from async-fixtures/inject-function-calls/index.txt rename to async_fixtures/inject-function-calls/index.txt diff --git a/async-fixtures/inject-function-calls/modal.edge b/async_fixtures/inject-function-calls/modal.edge similarity index 100% rename from async-fixtures/inject-function-calls/modal.edge rename to async_fixtures/inject-function-calls/modal.edge diff --git a/async-fixtures/inject-identifier/compiled.js b/async_fixtures/inject-identifier/compiled.js similarity index 100% rename from async-fixtures/inject-identifier/compiled.js rename to async_fixtures/inject-identifier/compiled.js diff --git a/async-fixtures/inject-identifier/index.edge b/async_fixtures/inject-identifier/index.edge similarity index 100% rename from async-fixtures/inject-identifier/index.edge rename to async_fixtures/inject-identifier/index.edge diff --git a/async-fixtures/inject-object-expression/index.json b/async_fixtures/inject-identifier/index.json similarity index 100% rename from async-fixtures/inject-object-expression/index.json rename to async_fixtures/inject-identifier/index.json diff --git a/async-fixtures/inject-identifier/index.txt b/async_fixtures/inject-identifier/index.txt similarity index 100% rename from async-fixtures/inject-identifier/index.txt rename to async_fixtures/inject-identifier/index.txt diff --git a/async-fixtures/inject-identifier/modal.edge b/async_fixtures/inject-identifier/modal.edge similarity index 100% rename from async-fixtures/inject-identifier/modal.edge rename to async_fixtures/inject-identifier/modal.edge diff --git a/async-fixtures/inject-object-expression/compiled.js b/async_fixtures/inject-object-expression/compiled.js similarity index 100% rename from async-fixtures/inject-object-expression/compiled.js rename to async_fixtures/inject-object-expression/compiled.js diff --git a/async-fixtures/inject-object-expression/index.edge b/async_fixtures/inject-object-expression/index.edge similarity index 100% rename from async-fixtures/inject-object-expression/index.edge rename to async_fixtures/inject-object-expression/index.edge diff --git a/async-fixtures/inject-object-with-references/index.json b/async_fixtures/inject-object-expression/index.json similarity index 100% rename from async-fixtures/inject-object-with-references/index.json rename to async_fixtures/inject-object-expression/index.json diff --git a/async-fixtures/inject-object-expression/index.txt b/async_fixtures/inject-object-expression/index.txt similarity index 100% rename from async-fixtures/inject-object-expression/index.txt rename to async_fixtures/inject-object-expression/index.txt diff --git a/async-fixtures/inject-object-expression/modal.edge b/async_fixtures/inject-object-expression/modal.edge similarity index 100% rename from async-fixtures/inject-object-expression/modal.edge rename to async_fixtures/inject-object-expression/modal.edge diff --git a/async-fixtures/inject-object-with-references/compiled.js b/async_fixtures/inject-object-with-references/compiled.js similarity index 100% rename from async-fixtures/inject-object-with-references/compiled.js rename to async_fixtures/inject-object-with-references/compiled.js diff --git a/async-fixtures/inject-object-with-references/index.edge b/async_fixtures/inject-object-with-references/index.edge similarity index 100% rename from async-fixtures/inject-object-with-references/index.edge rename to async_fixtures/inject-object-with-references/index.edge diff --git a/newline-fixtures/comments-multiline-prefix/index.json b/async_fixtures/inject-object-with-references/index.json similarity index 100% rename from newline-fixtures/comments-multiline-prefix/index.json rename to async_fixtures/inject-object-with-references/index.json diff --git a/async-fixtures/inject-object-with-references/index.txt b/async_fixtures/inject-object-with-references/index.txt similarity index 100% rename from async-fixtures/inject-object-with-references/index.txt rename to async_fixtures/inject-object-with-references/index.txt diff --git a/async-fixtures/inject-object-with-references/modal.edge b/async_fixtures/inject-object-with-references/modal.edge similarity index 100% rename from async-fixtures/inject-object-with-references/modal.edge rename to async_fixtures/inject-object-with-references/modal.edge diff --git a/async-fixtures/layout-allow-set-calls/compiled.js b/async_fixtures/layout-allow-set-calls-compat/compiled.js similarity index 100% rename from async-fixtures/layout-allow-set-calls/compiled.js rename to async_fixtures/layout-allow-set-calls-compat/compiled.js diff --git a/async_fixtures/layout-allow-set-calls-compat/index.edge b/async_fixtures/layout-allow-set-calls-compat/index.edge new file mode 100644 index 0000000..1aa23fa --- /dev/null +++ b/async_fixtures/layout-allow-set-calls-compat/index.edge @@ -0,0 +1,2 @@ +@layout("layout-allow-set-calls-compat/master") +@set("username", "virk") \ No newline at end of file diff --git a/async-fixtures/layout-allow-set-calls/index.json b/async_fixtures/layout-allow-set-calls-compat/index.json similarity index 100% rename from async-fixtures/layout-allow-set-calls/index.json rename to async_fixtures/layout-allow-set-calls-compat/index.json diff --git a/async-fixtures/layout-allow-set-calls/index.txt b/async_fixtures/layout-allow-set-calls-compat/index.txt similarity index 100% rename from async-fixtures/layout-allow-set-calls/index.txt rename to async_fixtures/layout-allow-set-calls-compat/index.txt diff --git a/async-fixtures/layout-allow-set-calls/master.edge b/async_fixtures/layout-allow-set-calls-compat/master.edge similarity index 100% rename from async-fixtures/layout-allow-set-calls/master.edge rename to async_fixtures/layout-allow-set-calls-compat/master.edge diff --git a/async-fixtures/layout-nested/compiled.js b/async_fixtures/layout-nested-compat/compiled.js similarity index 100% rename from async-fixtures/layout-nested/compiled.js rename to async_fixtures/layout-nested-compat/compiled.js diff --git a/fixtures/layout-nested/index.edge b/async_fixtures/layout-nested-compat/index.edge similarity index 58% rename from fixtures/layout-nested/index.edge rename to async_fixtures/layout-nested-compat/index.edge index df5ee99..04cd27d 100644 --- a/fixtures/layout-nested/index.edge +++ b/async_fixtures/layout-nested-compat/index.edge @@ -1,4 +1,4 @@ -@layout("layout-nested/master") +@layout("layout-nested-compat/master") @section("content") @super Appended by index diff --git a/async-fixtures/layout-nested/index.json b/async_fixtures/layout-nested-compat/index.json similarity index 100% rename from async-fixtures/layout-nested/index.json rename to async_fixtures/layout-nested-compat/index.json diff --git a/async-fixtures/layout-nested/index.txt b/async_fixtures/layout-nested-compat/index.txt similarity index 100% rename from async-fixtures/layout-nested/index.txt rename to async_fixtures/layout-nested-compat/index.txt diff --git a/async-fixtures/layout-nested/master.edge b/async_fixtures/layout-nested-compat/master.edge similarity index 62% rename from async-fixtures/layout-nested/master.edge rename to async_fixtures/layout-nested-compat/master.edge index 9f77b36..edcc78a 100644 --- a/async-fixtures/layout-nested/master.edge +++ b/async_fixtures/layout-nested-compat/master.edge @@ -1,4 +1,4 @@ -@layout("layout-nested/super") +@layout("layout-nested-compat/super") @section("header") I will define the header myself @endsection diff --git a/async-fixtures/layout-nested/super.edge b/async_fixtures/layout-nested-compat/super.edge similarity index 100% rename from async-fixtures/layout-nested/super.edge rename to async_fixtures/layout-nested-compat/super.edge diff --git a/async-fixtures/layout-tag/compiled.js b/async_fixtures/layout-tag-compat/compiled.js similarity index 100% rename from async-fixtures/layout-tag/compiled.js rename to async_fixtures/layout-tag-compat/compiled.js diff --git a/async_fixtures/layout-tag-compat/index.edge b/async_fixtures/layout-tag-compat/index.edge new file mode 100644 index 0000000..f8e3440 --- /dev/null +++ b/async_fixtures/layout-tag-compat/index.edge @@ -0,0 +1 @@ +@layout("layout-tag-compat/master") \ No newline at end of file diff --git a/async-fixtures/layout-tag-extend-section/index.json b/async_fixtures/layout-tag-compat/index.json similarity index 100% rename from async-fixtures/layout-tag-extend-section/index.json rename to async_fixtures/layout-tag-compat/index.json diff --git a/async-fixtures/layout-tag/index.txt b/async_fixtures/layout-tag-compat/index.txt similarity index 100% rename from async-fixtures/layout-tag/index.txt rename to async_fixtures/layout-tag-compat/index.txt diff --git a/async-fixtures/layout-tag/master.edge b/async_fixtures/layout-tag-compat/master.edge similarity index 100% rename from async-fixtures/layout-tag/master.edge rename to async_fixtures/layout-tag-compat/master.edge diff --git a/async-fixtures/layout-tag-extend-section/compiled.js b/async_fixtures/layout-tag-extend-section-compat/compiled.js similarity index 100% rename from async-fixtures/layout-tag-extend-section/compiled.js rename to async_fixtures/layout-tag-extend-section-compat/compiled.js diff --git a/async-fixtures/layout-tag-extend-section/index.edge b/async_fixtures/layout-tag-extend-section-compat/index.edge similarity index 58% rename from async-fixtures/layout-tag-extend-section/index.edge rename to async_fixtures/layout-tag-extend-section-compat/index.edge index 74c884e..ff52d16 100644 --- a/async-fixtures/layout-tag-extend-section/index.edge +++ b/async_fixtures/layout-tag-extend-section-compat/index.edge @@ -1,4 +1,4 @@ -@layout("layout-tag-extend-section/master") +@layout("layout-tag-extend-section-compat/master") @section("content") @super Hello {{ username }} from children diff --git a/async-fixtures/layout-tag-multiple-sections/index.json b/async_fixtures/layout-tag-extend-section-compat/index.json similarity index 100% rename from async-fixtures/layout-tag-multiple-sections/index.json rename to async_fixtures/layout-tag-extend-section-compat/index.json diff --git a/async-fixtures/layout-tag-extend-section/index.txt b/async_fixtures/layout-tag-extend-section-compat/index.txt similarity index 100% rename from async-fixtures/layout-tag-extend-section/index.txt rename to async_fixtures/layout-tag-extend-section-compat/index.txt diff --git a/async-fixtures/layout-tag-extend-section/master.edge b/async_fixtures/layout-tag-extend-section-compat/master.edge similarity index 100% rename from async-fixtures/layout-tag-extend-section/master.edge rename to async_fixtures/layout-tag-extend-section-compat/master.edge diff --git a/async-fixtures/layout-tag-multiple-sections/compiled.js b/async_fixtures/layout-tag-multiple-sections-compat/compiled.js similarity index 100% rename from async-fixtures/layout-tag-multiple-sections/compiled.js rename to async_fixtures/layout-tag-multiple-sections-compat/compiled.js diff --git a/fixtures/layout-tag-multiple-sections/index.edge b/async_fixtures/layout-tag-multiple-sections-compat/index.edge similarity index 66% rename from fixtures/layout-tag-multiple-sections/index.edge rename to async_fixtures/layout-tag-multiple-sections-compat/index.edge index ddba73e..f8fa731 100644 --- a/fixtures/layout-tag-multiple-sections/index.edge +++ b/async_fixtures/layout-tag-multiple-sections-compat/index.edge @@ -1,4 +1,4 @@ -@layout("layout-tag-multiple-sections/master") +@layout("layout-tag-multiple-sections-compat/master") @section("content") I will override the content diff --git a/async-fixtures/layout-tag-override-sections/index.json b/async_fixtures/layout-tag-multiple-sections-compat/index.json similarity index 100% rename from async-fixtures/layout-tag-override-sections/index.json rename to async_fixtures/layout-tag-multiple-sections-compat/index.json diff --git a/async-fixtures/layout-tag-multiple-sections/index.txt b/async_fixtures/layout-tag-multiple-sections-compat/index.txt similarity index 100% rename from async-fixtures/layout-tag-multiple-sections/index.txt rename to async_fixtures/layout-tag-multiple-sections-compat/index.txt diff --git a/async-fixtures/layout-tag-multiple-sections/master.edge b/async_fixtures/layout-tag-multiple-sections-compat/master.edge similarity index 100% rename from async-fixtures/layout-tag-multiple-sections/master.edge rename to async_fixtures/layout-tag-multiple-sections-compat/master.edge diff --git a/async-fixtures/layout-tag-override-sections/compiled.js b/async_fixtures/layout-tag-override-sections-compat/compiled.js similarity index 100% rename from async-fixtures/layout-tag-override-sections/compiled.js rename to async_fixtures/layout-tag-override-sections-compat/compiled.js diff --git a/fixtures/layout-tag-override-sections/index.edge b/async_fixtures/layout-tag-override-sections-compat/index.edge similarity index 54% rename from fixtures/layout-tag-override-sections/index.edge rename to async_fixtures/layout-tag-override-sections-compat/index.edge index 8fa32cc..7aec0c0 100644 --- a/fixtures/layout-tag-override-sections/index.edge +++ b/async_fixtures/layout-tag-override-sections-compat/index.edge @@ -1,4 +1,4 @@ -@layout("layout-tag-override-sections/master") +@layout("layout-tag-override-sections-compat/master") @section("content") I will override the base content diff --git a/async-fixtures/layout-tag-sections/index.json b/async_fixtures/layout-tag-override-sections-compat/index.json similarity index 100% rename from async-fixtures/layout-tag-sections/index.json rename to async_fixtures/layout-tag-override-sections-compat/index.json diff --git a/async-fixtures/layout-tag-override-sections/index.txt b/async_fixtures/layout-tag-override-sections-compat/index.txt similarity index 100% rename from async-fixtures/layout-tag-override-sections/index.txt rename to async_fixtures/layout-tag-override-sections-compat/index.txt diff --git a/async-fixtures/layout-tag-override-sections/master.edge b/async_fixtures/layout-tag-override-sections-compat/master.edge similarity index 100% rename from async-fixtures/layout-tag-override-sections/master.edge rename to async_fixtures/layout-tag-override-sections-compat/master.edge diff --git a/async-fixtures/layout-tag-sections/compiled.js b/async_fixtures/layout-tag-sections-compat/compiled.js similarity index 100% rename from async-fixtures/layout-tag-sections/compiled.js rename to async_fixtures/layout-tag-sections-compat/compiled.js diff --git a/async_fixtures/layout-tag-sections-compat/index.edge b/async_fixtures/layout-tag-sections-compat/index.edge new file mode 100644 index 0000000..ae7bb82 --- /dev/null +++ b/async_fixtures/layout-tag-sections-compat/index.edge @@ -0,0 +1 @@ +@layout("layout-tag-sections-compat/master") \ No newline at end of file diff --git a/async-fixtures/layout-tag/index.json b/async_fixtures/layout-tag-sections-compat/index.json similarity index 100% rename from async-fixtures/layout-tag/index.json rename to async_fixtures/layout-tag-sections-compat/index.json diff --git a/async-fixtures/layout-tag-sections/index.txt b/async_fixtures/layout-tag-sections-compat/index.txt similarity index 100% rename from async-fixtures/layout-tag-sections/index.txt rename to async_fixtures/layout-tag-sections-compat/index.txt diff --git a/async-fixtures/layout-tag-sections/master.edge b/async_fixtures/layout-tag-sections-compat/master.edge similarity index 100% rename from async-fixtures/layout-tag-sections/master.edge rename to async_fixtures/layout-tag-sections-compat/master.edge diff --git a/async-fixtures/nested-components/alert.edge b/async_fixtures/nested-components/alert.edge similarity index 100% rename from async-fixtures/nested-components/alert.edge rename to async_fixtures/nested-components/alert.edge diff --git a/async-fixtures/nested-components/compiled.js b/async_fixtures/nested-components/compiled.js similarity index 100% rename from async-fixtures/nested-components/compiled.js rename to async_fixtures/nested-components/compiled.js diff --git a/async-fixtures/nested-components/index.edge b/async_fixtures/nested-components/index.edge similarity index 100% rename from async-fixtures/nested-components/index.edge rename to async_fixtures/nested-components/index.edge diff --git a/async-fixtures/nested-components/index.json b/async_fixtures/nested-components/index.json similarity index 100% rename from async-fixtures/nested-components/index.json rename to async_fixtures/nested-components/index.json diff --git a/async-fixtures/nested-components/index.txt b/async_fixtures/nested-components/index.txt similarity index 100% rename from async-fixtures/nested-components/index.txt rename to async_fixtures/nested-components/index.txt diff --git a/async-fixtures/nested-components/success.edge b/async_fixtures/nested-components/success.edge similarity index 100% rename from async-fixtures/nested-components/success.edge rename to async_fixtures/nested-components/success.edge diff --git a/async-fixtures/nested-if-tag/compiled.js b/async_fixtures/nested-if-tag/compiled.js similarity index 100% rename from async-fixtures/nested-if-tag/compiled.js rename to async_fixtures/nested-if-tag/compiled.js diff --git a/async-fixtures/nested-if-tag/index.edge b/async_fixtures/nested-if-tag/index.edge similarity index 100% rename from async-fixtures/nested-if-tag/index.edge rename to async_fixtures/nested-if-tag/index.edge diff --git a/async-fixtures/nested-if-tag/index.json b/async_fixtures/nested-if-tag/index.json similarity index 100% rename from async-fixtures/nested-if-tag/index.json rename to async_fixtures/nested-if-tag/index.json diff --git a/async-fixtures/nested-if-tag/index.txt b/async_fixtures/nested-if-tag/index.txt similarity index 100% rename from async-fixtures/nested-if-tag/index.txt rename to async_fixtures/nested-if-tag/index.txt diff --git a/async-fixtures/set-tag-identifier/compiled.js b/async_fixtures/set-tag-identifier/compiled.js similarity index 100% rename from async-fixtures/set-tag-identifier/compiled.js rename to async_fixtures/set-tag-identifier/compiled.js diff --git a/async-fixtures/set-tag-identifier/index.edge b/async_fixtures/set-tag-identifier/index.edge similarity index 100% rename from async-fixtures/set-tag-identifier/index.edge rename to async_fixtures/set-tag-identifier/index.edge diff --git a/async-fixtures/set-tag-identifier/index.json b/async_fixtures/set-tag-identifier/index.json similarity index 100% rename from async-fixtures/set-tag-identifier/index.json rename to async_fixtures/set-tag-identifier/index.json diff --git a/async-fixtures/set-tag-identifier/index.txt b/async_fixtures/set-tag-identifier/index.txt similarity index 100% rename from async-fixtures/set-tag-identifier/index.txt rename to async_fixtures/set-tag-identifier/index.txt diff --git a/async-fixtures/set-tag-update-value/compiled.js b/async_fixtures/set-tag-update-value/compiled.js similarity index 100% rename from async-fixtures/set-tag-update-value/compiled.js rename to async_fixtures/set-tag-update-value/compiled.js diff --git a/async-fixtures/set-tag-update-value/index.edge b/async_fixtures/set-tag-update-value/index.edge similarity index 100% rename from async-fixtures/set-tag-update-value/index.edge rename to async_fixtures/set-tag-update-value/index.edge diff --git a/async-fixtures/set-tag-update-value/index.json b/async_fixtures/set-tag-update-value/index.json similarity index 100% rename from async-fixtures/set-tag-update-value/index.json rename to async_fixtures/set-tag-update-value/index.json diff --git a/async-fixtures/set-tag-update-value/index.txt b/async_fixtures/set-tag-update-value/index.txt similarity index 100% rename from async-fixtures/set-tag-update-value/index.txt rename to async_fixtures/set-tag-update-value/index.txt diff --git a/async-fixtures/set-tag/compiled.js b/async_fixtures/set-tag/compiled.js similarity index 100% rename from async-fixtures/set-tag/compiled.js rename to async_fixtures/set-tag/compiled.js diff --git a/async-fixtures/set-tag/index.edge b/async_fixtures/set-tag/index.edge similarity index 100% rename from async-fixtures/set-tag/index.edge rename to async_fixtures/set-tag/index.edge diff --git a/async-fixtures/set-tag/index.json b/async_fixtures/set-tag/index.json similarity index 100% rename from async-fixtures/set-tag/index.json rename to async_fixtures/set-tag/index.json diff --git a/async-fixtures/set-tag/index.txt b/async_fixtures/set-tag/index.txt similarity index 100% rename from async-fixtures/set-tag/index.txt rename to async_fixtures/set-tag/index.txt diff --git a/async-fixtures/unless-tag/compiled.js b/async_fixtures/unless-tag/compiled.js similarity index 100% rename from async-fixtures/unless-tag/compiled.js rename to async_fixtures/unless-tag/compiled.js diff --git a/async-fixtures/unless-tag/index.edge b/async_fixtures/unless-tag/index.edge similarity index 100% rename from async-fixtures/unless-tag/index.edge rename to async_fixtures/unless-tag/index.edge diff --git a/async-fixtures/unless-tag/index.json b/async_fixtures/unless-tag/index.json similarity index 100% rename from async-fixtures/unless-tag/index.json rename to async_fixtures/unless-tag/index.json diff --git a/async-fixtures/unless-tag/index.txt b/async_fixtures/unless-tag/index.txt similarity index 100% rename from async-fixtures/unless-tag/index.txt rename to async_fixtures/unless-tag/index.txt diff --git a/bin/japaTypes.ts b/bin/japaTypes.ts deleted file mode 100644 index f4c7bff..0000000 --- a/bin/japaTypes.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Assert } from '@japa/assert' - -declare module '@japa/runner' { - interface TestContext { - assert: Assert - } -} diff --git a/bin/test.ts b/bin/test.ts index 5aba7ce..278446c 100644 --- a/bin/test.ts +++ b/bin/test.ts @@ -1,7 +1,7 @@ import { assert } from '@japa/assert' -import { specReporter } from '@japa/spec-reporter' -import { runFailedTests } from '@japa/run-failed-tests' -import { processCliArgs, configure, run } from '@japa/runner' +import { processCLIArgs, configure, run } from '@japa/runner' +import { fileSystem } from '@japa/file-system' +import { BASE_URL } from '../tests_helpers/index.js' /* |-------------------------------------------------------------------------- @@ -16,14 +16,10 @@ import { processCliArgs, configure, run } from '@japa/runner' | | Please consult japa.dev/runner-config for the config docs. */ +processCLIArgs(process.argv.slice(2)) configure({ - ...processCliArgs(process.argv.slice(2)), - ...{ - files: ['test/**/*.spec.ts'], - plugins: [assert(), runFailedTests()], - reporters: [specReporter()], - importer: (filePath: string) => import(filePath), - }, + files: ['tests/**/*.spec.ts'], + plugins: [assert(), fileSystem({ basePath: BASE_URL })], }) /* diff --git a/example/index.ts b/example/index.ts deleted file mode 100644 index bfcc278..0000000 --- a/example/index.ts +++ /dev/null @@ -1,45 +0,0 @@ -import edge from '../index' -import { join } from 'path' -import { createServer } from 'http' - -edge.mount(join(__dirname, 'views')) - -class Base { - public isModel = true - public foo = true -} - -class User extends Base { - public attributes = { - username: 'virk', - email: 'virk@adonisjs.com', - isAdmin: true, - profile: { - avatarUrl: 'foo', - }, - lastLoginAt: null, - } - - public parent: User - public get username() { - return this.attributes.username - } - - public toJSON() { - return {} - } -} - -const user = new User() -user.parent = user - -createServer(async (_req, res) => { - res.writeHead(200, { 'content-type': 'text/html' }) - res.end( - await edge.render('welcome', { - user: user, - }) - ) -}).listen(3000, () => { - console.log('Listening on 127.0.0.1:3000') -}) diff --git a/example/views/components/modal.edge b/example/views/components/modal.edge deleted file mode 100644 index d40c328..0000000 --- a/example/views/components/modal.edge +++ /dev/null @@ -1,12 +0,0 @@ -{{ inspect(state) }} - - diff --git a/example/views/partials/button.edge b/example/views/partials/button.edge deleted file mode 100644 index 5fcb8d4..0000000 --- a/example/views/partials/button.edge +++ /dev/null @@ -1 +0,0 @@ - diff --git a/examples/index.ts b/examples/index.ts new file mode 100644 index 0000000..8e9e727 --- /dev/null +++ b/examples/index.ts @@ -0,0 +1,49 @@ +import { Edge } from '../index.js' +import { join } from 'node:path' +import { createServer } from 'node:http' +import { getDirname } from '@poppinss/utils' +import { migrate } from '../src/migrate/plugin.js' + +const edge = Edge.create() +edge.use(migrate) +edge.mount(join(getDirname(import.meta.url), 'views')) + +class Base { + isModel = true + foo = true +} + +class User extends Base { + attributes = { + username: 'virk', + email: 'virk@adonisjs.com', + isAdmin: true, + profile: { + avatarUrl: 'foo', + }, + lastLoginAt: null, + } + + parent!: User + get username() { + return this.attributes.username + } + + toJSON() { + return {} + } +} + +const user = new User() +user.parent = user + +createServer(async (_req, res) => { + res.writeHead(200, { 'content-type': 'text/html' }) + const html = await edge.render('welcome', { + user: user, + }) + console.log(html) + res.end(html) +}).listen(3000, () => { + console.log('Listening on 127.0.0.1:3000') +}) diff --git a/examples/views/components/modal.edge b/examples/views/components/modal.edge new file mode 100644 index 0000000..aa94ac0 --- /dev/null +++ b/examples/views/components/modal.edge @@ -0,0 +1,27 @@ +{{ inspect(state) }} + +
+

{{ title }}

+
+ {{{ await $slots.body() }}} +
+ +
+ {{{ await $slots.actions() }}} +
+
diff --git a/examples/views/partials/button.edge b/examples/views/partials/button.edge new file mode 100644 index 0000000..2ef136f --- /dev/null +++ b/examples/views/partials/button.edge @@ -0,0 +1 @@ + diff --git a/example/views/welcome.edge b/examples/views/welcome.edge similarity index 79% rename from example/views/welcome.edge rename to examples/views/welcome.edge index dd9618a..73e1541 100644 --- a/example/views/welcome.edge +++ b/examples/views/welcome.edge @@ -3,6 +3,7 @@ + {{ inspect(state.user) }} diff --git a/fixtures/components-advanced-props-compat/button.edge b/fixtures/components-advanced-props-compat/button.edge new file mode 100644 index 0000000..ee08504 --- /dev/null +++ b/fixtures/components-advanced-props-compat/button.edge @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/fixtures/components-advanced-props-compat/compiled.js b/fixtures/components-advanced-props-compat/compiled.js new file mode 100644 index 0000000..66656bc --- /dev/null +++ b/fixtures/components-advanced-props-compat/compiled.js @@ -0,0 +1,13 @@ +let out = ""; +let $lineNumber = 1; +let $filename = "{{__dirname}}index.edge"; +try { +out += template.compileComponent("components-advanced-props-compat/button")(template, template.getComponentState({ + class: 'mb-4 px-4', + id: 'foo-bar', + title: 'Click me' +}, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); +} catch (error) { +template.reThrow(error, $filename, $lineNumber); +} +return out; \ No newline at end of file diff --git a/fixtures/components-advanced-props-compat/index.edge b/fixtures/components-advanced-props-compat/index.edge new file mode 100644 index 0000000..ffc0f76 --- /dev/null +++ b/fixtures/components-advanced-props-compat/index.edge @@ -0,0 +1,5 @@ +@!component("components-advanced-props-compat/button", { + class: 'mb-4 px-4', + id: 'foo-bar', + title: 'Click me' +}) \ No newline at end of file diff --git a/newline-fixtures/comments-multiline-suffix/index.json b/fixtures/components-advanced-props-compat/index.json similarity index 100% rename from newline-fixtures/comments-multiline-suffix/index.json rename to fixtures/components-advanced-props-compat/index.json diff --git a/fixtures/components-advanced-props-compat/index.txt b/fixtures/components-advanced-props-compat/index.txt new file mode 100644 index 0000000..97fb58a --- /dev/null +++ b/fixtures/components-advanced-props-compat/index.txt @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/fixtures/components-advanced-props/button.edge b/fixtures/components-advanced-props/button.edge index ee08504..fa6fed0 100644 --- a/fixtures/components-advanced-props/button.edge +++ b/fixtures/components-advanced-props/button.edge @@ -1,3 +1,3 @@ - \ No newline at end of file diff --git a/fixtures/components-advanced-props/compiled.js b/fixtures/components-advanced-props/compiled.js index 6a767a6..6940a28 100644 --- a/fixtures/components-advanced-props/compiled.js +++ b/fixtures/components-advanced-props/compiled.js @@ -2,7 +2,11 @@ let out = ""; let $lineNumber = 1; let $filename = "{{__dirname}}index.edge"; try { -out += template.compileComponent("components-advanced-props/button")(template, template.getComponentState({ class: 'mb-4 px-4', id: 'foo-bar', title: 'Click me' }, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); +out += template.compileComponent("components-advanced-props/button")(template, template.getComponentState({ + class: 'mb-4 px-4', + id: 'foo-bar', + title: 'Click me' +}, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); } catch (error) { template.reThrow(error, $filename, $lineNumber); } diff --git a/fixtures/components-falsy-args/compiled.js b/fixtures/components-falsy-args/compiled.js index 8af28a4..84f45df 100644 --- a/fixtures/components-falsy-args/compiled.js +++ b/fixtures/components-falsy-args/compiled.js @@ -2,7 +2,9 @@ let out = ""; let $lineNumber = 1; let $filename = "{{__dirname}}index.edge"; try { -out += template.compileComponent("components-falsy-args/alert")(template, template.getComponentState({ index: 0 }, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); +out += template.compileComponent("components-falsy-args/alert")(template, template.getComponentState({ + index: 0 +}, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); } catch (error) { template.reThrow(error, $filename, $lineNumber); } diff --git a/fixtures/components-falsy-args/index.edge b/fixtures/components-falsy-args/index.edge index a20e150..93dbe7d 100644 --- a/fixtures/components-falsy-args/index.edge +++ b/fixtures/components-falsy-args/index.edge @@ -1 +1 @@ -@!component("components-falsy-args/alert", index = 0) \ No newline at end of file +@!component("components-falsy-args/alert", { index: 0 }) \ No newline at end of file diff --git a/fixtures/components-partials/compiled.js b/fixtures/components-partials/compiled.js index 0b9253f..6e95014 100644 --- a/fixtures/components-partials/compiled.js +++ b/fixtures/components-partials/compiled.js @@ -2,7 +2,9 @@ let out = ""; let $lineNumber = 1; let $filename = "{{__dirname}}index.edge"; try { -out += template.compileComponent("components-partials/alert")(template, template.getComponentState({ username: "virk" }, { $context: Object.assign({}, $context), main: function () { const $context = this.$context; +out += template.compileComponent("components-partials/alert")(template, template.getComponentState({ + username: 'virk' +}, { $context: Object.assign({}, $context), main: function () { const $context = this.$context; let slot_main = ""; try { slot_main += " Hello "; diff --git a/fixtures/components-partials/index.edge b/fixtures/components-partials/index.edge index c185bff..6e11f8b 100644 --- a/fixtures/components-partials/index.edge +++ b/fixtures/components-partials/index.edge @@ -1,3 +1,3 @@ -@component("components-partials/alert", username = "virk") +@component("components-partials/alert", { username: 'virk' }) Hello {{ username || "Guest" }} @endcomponent \ No newline at end of file diff --git a/fixtures/components-props/compiled.js b/fixtures/components-props/compiled.js index 5836071..4dc8930 100644 --- a/fixtures/components-props/compiled.js +++ b/fixtures/components-props/compiled.js @@ -2,7 +2,9 @@ let out = ""; let $lineNumber = 1; let $filename = "{{__dirname}}index.edge"; try { -out += template.compileComponent("components-props/alert")(template, template.getComponentState({ "title": "H1" }, { $context: Object.assign({}, $context), main: function () { const $context = this.$context; +out += template.compileComponent("components-props/alert")(template, template.getComponentState({ + "title": "H1" +}, { $context: Object.assign({}, $context), main: function () { const $context = this.$context; let slot_main = ""; try { slot_main += "Hello world"; diff --git a/fixtures/components-spread-and-literal/compiled.js b/fixtures/components-spread-and-literal/compiled.js index 809a3c0..8863737 100644 --- a/fixtures/components-spread-and-literal/compiled.js +++ b/fixtures/components-spread-and-literal/compiled.js @@ -2,7 +2,10 @@ let out = ""; let $lineNumber = 1; let $filename = "{{__dirname}}index.edge"; try { -out += template.compileComponent("components-spread-and-literal/alert")(template, template.getComponentState({ ...state.data, name: 'virk' }, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); +out += template.compileComponent("components-spread-and-literal/alert")(template, template.getComponentState({ + ...state.data, + name: 'virk' +}, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); } catch (error) { template.reThrow(error, $filename, $lineNumber); } diff --git a/fixtures/components-spread/compiled.js b/fixtures/components-spread/compiled.js index ec99c8a..3cad914 100644 --- a/fixtures/components-spread/compiled.js +++ b/fixtures/components-spread/compiled.js @@ -2,7 +2,9 @@ let out = ""; let $lineNumber = 1; let $filename = "{{__dirname}}index.edge"; try { -out += template.compileComponent("components-spread/alert")(template, template.getComponentState({ ...state.data }, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); +out += template.compileComponent("components-spread/alert")(template, template.getComponentState({ + ...state.data +}, { $context: Object.assign({}, $context), main: function () { return "" } }, { filename: $filename, line: $lineNumber, col: 0 }), $context); } catch (error) { template.reThrow(error, $filename, $lineNumber); } diff --git a/fixtures/each-loop-set/compiled.js b/fixtures/each-loop-set-compat/compiled.js similarity index 100% rename from fixtures/each-loop-set/compiled.js rename to fixtures/each-loop-set-compat/compiled.js diff --git a/fixtures/each-loop-set/index.edge b/fixtures/each-loop-set-compat/index.edge similarity index 100% rename from fixtures/each-loop-set/index.edge rename to fixtures/each-loop-set-compat/index.edge diff --git a/fixtures/each-loop-set/index.json b/fixtures/each-loop-set-compat/index.json similarity index 100% rename from fixtures/each-loop-set/index.json rename to fixtures/each-loop-set-compat/index.json diff --git a/fixtures/each-loop-set/index.txt b/fixtures/each-loop-set-compat/index.txt similarity index 100% rename from fixtures/each-loop-set/index.txt rename to fixtures/each-loop-set-compat/index.txt diff --git a/fixtures/eval-tag-push-to-array/compiled.js b/fixtures/eval-tag-push-to-array/compiled.js new file mode 100644 index 0000000..2b01a31 --- /dev/null +++ b/fixtures/eval-tag-push-to-array/compiled.js @@ -0,0 +1,18 @@ +let out = ""; +let $lineNumber = 1; +let $filename = "{{__dirname}}index.edge"; +try { +state.users.push({ + username: 'romain' +}); +$lineNumber = 2; +template.loop(state.users, function (user) { +out += "\n"; +out += " - "; +$lineNumber = 3; +out += `${template.escape(user.username)}`; +}); +} catch (error) { +template.reThrow(error, $filename, $lineNumber); +} +return out; \ No newline at end of file diff --git a/fixtures/eval-tag-push-to-array/index.edge b/fixtures/eval-tag-push-to-array/index.edge new file mode 100644 index 0000000..fc481c0 --- /dev/null +++ b/fixtures/eval-tag-push-to-array/index.edge @@ -0,0 +1,4 @@ +@eval(users.push({ username: 'romain' })) +@each(user in users) + - {{ user.username }} +@end \ No newline at end of file diff --git a/fixtures/eval-tag-push-to-array/index.json b/fixtures/eval-tag-push-to-array/index.json new file mode 100644 index 0000000..aff6f8a --- /dev/null +++ b/fixtures/eval-tag-push-to-array/index.json @@ -0,0 +1,3 @@ +{ + "users": [{ "username": "virk" }] +} \ No newline at end of file diff --git a/fixtures/eval-tag-push-to-array/index.txt b/fixtures/eval-tag-push-to-array/index.txt new file mode 100644 index 0000000..2f96ab3 --- /dev/null +++ b/fixtures/eval-tag-push-to-array/index.txt @@ -0,0 +1,2 @@ +- virk + - romain \ No newline at end of file diff --git a/fixtures/eval-tag/compiled.js b/fixtures/eval-tag/compiled.js new file mode 100644 index 0000000..1ec0f29 --- /dev/null +++ b/fixtures/eval-tag/compiled.js @@ -0,0 +1,12 @@ +let out = ""; +let $lineNumber = 1; +let $filename = "{{__dirname}}index.edge"; +try { +state.user.username = state.user.username.toUpperCase(); +out += "Hello "; +$lineNumber = 2; +out += `${template.escape(state.user.username)}`; +} catch (error) { +template.reThrow(error, $filename, $lineNumber); +} +return out; \ No newline at end of file diff --git a/fixtures/eval-tag/index.edge b/fixtures/eval-tag/index.edge new file mode 100644 index 0000000..09f67d2 --- /dev/null +++ b/fixtures/eval-tag/index.edge @@ -0,0 +1,2 @@ +@eval(user.username = user.username.toUpperCase()) +Hello {{ user.username }} \ No newline at end of file diff --git a/fixtures/eval-tag/index.json b/fixtures/eval-tag/index.json new file mode 100644 index 0000000..9c47b3a --- /dev/null +++ b/fixtures/eval-tag/index.json @@ -0,0 +1,5 @@ +{ + "user": { + "username": "virk" + } +} \ No newline at end of file diff --git a/fixtures/eval-tag/index.txt b/fixtures/eval-tag/index.txt new file mode 100644 index 0000000..14edf7f --- /dev/null +++ b/fixtures/eval-tag/index.txt @@ -0,0 +1 @@ +Hello VIRK \ No newline at end of file diff --git a/fixtures/inject-function-calls/compiled.js b/fixtures/inject-function-calls/compiled.js index 02c141e..0ea477c 100644 --- a/fixtures/inject-function-calls/compiled.js +++ b/fixtures/inject-function-calls/compiled.js @@ -6,7 +6,7 @@ out += template.compileComponent('inject-function-calls/modal')(template, templa let slot_main = ""; try { $lineNumber = 2; -slot_main += `${template.escape($context.foo())}`; +slot_main += `${template.escape($context.foo)}`; } catch (error) { template.reThrow(error, $filename, $lineNumber); } diff --git a/fixtures/inject-function-calls/index.edge b/fixtures/inject-function-calls/index.edge index 453e846..be525e7 100644 --- a/fixtures/inject-function-calls/index.edge +++ b/fixtures/inject-function-calls/index.edge @@ -1,3 +1,3 @@ @component('inject-function-calls/modal') -{{ $context.foo() }} +{{ $context.foo }} @end \ No newline at end of file diff --git a/fixtures/inject-function-calls/index.txt b/fixtures/inject-function-calls/index.txt index f6ea049..ba0e162 100644 --- a/fixtures/inject-function-calls/index.txt +++ b/fixtures/inject-function-calls/index.txt @@ -1 +1 @@ -foobar \ No newline at end of file +bar \ No newline at end of file diff --git a/fixtures/inject-function-calls/modal.edge b/fixtures/inject-function-calls/modal.edge index 90f629d..f959ad7 100644 --- a/fixtures/inject-function-calls/modal.edge +++ b/fixtures/inject-function-calls/modal.edge @@ -1,3 +1,7 @@ -@set('foo', 'foobar') -@inject({ foo: () => foo }) +@set('getData', () => { + return { + foo: 'bar' + } +}) +@inject(getData()) {{{ $slots.main() }}} \ No newline at end of file diff --git a/fixtures/layout-allow-set-calls/compiled.js b/fixtures/layout-allow-set-calls-compat/compiled.js similarity index 100% rename from fixtures/layout-allow-set-calls/compiled.js rename to fixtures/layout-allow-set-calls-compat/compiled.js diff --git a/fixtures/layout-allow-set-calls-compat/index.edge b/fixtures/layout-allow-set-calls-compat/index.edge new file mode 100644 index 0000000..1aa23fa --- /dev/null +++ b/fixtures/layout-allow-set-calls-compat/index.edge @@ -0,0 +1,2 @@ +@layout("layout-allow-set-calls-compat/master") +@set("username", "virk") \ No newline at end of file diff --git a/fixtures/layout-allow-set-calls/index.json b/fixtures/layout-allow-set-calls-compat/index.json similarity index 100% rename from fixtures/layout-allow-set-calls/index.json rename to fixtures/layout-allow-set-calls-compat/index.json diff --git a/fixtures/layout-allow-set-calls/index.txt b/fixtures/layout-allow-set-calls-compat/index.txt similarity index 100% rename from fixtures/layout-allow-set-calls/index.txt rename to fixtures/layout-allow-set-calls-compat/index.txt diff --git a/fixtures/layout-allow-set-calls/master.edge b/fixtures/layout-allow-set-calls-compat/master.edge similarity index 100% rename from fixtures/layout-allow-set-calls/master.edge rename to fixtures/layout-allow-set-calls-compat/master.edge diff --git a/fixtures/layout-allow-set-calls/index.edge b/fixtures/layout-allow-set-calls/index.edge deleted file mode 100644 index 6ffeaf0..0000000 --- a/fixtures/layout-allow-set-calls/index.edge +++ /dev/null @@ -1,2 +0,0 @@ -@layout("layout-allow-set-calls/master") -@set("username", "virk") \ No newline at end of file diff --git a/fixtures/layout-nested/compiled.js b/fixtures/layout-nested-compat/compiled.js similarity index 100% rename from fixtures/layout-nested/compiled.js rename to fixtures/layout-nested-compat/compiled.js diff --git a/async-fixtures/layout-nested/index.edge b/fixtures/layout-nested-compat/index.edge similarity index 58% rename from async-fixtures/layout-nested/index.edge rename to fixtures/layout-nested-compat/index.edge index df5ee99..04cd27d 100644 --- a/async-fixtures/layout-nested/index.edge +++ b/fixtures/layout-nested-compat/index.edge @@ -1,4 +1,4 @@ -@layout("layout-nested/master") +@layout("layout-nested-compat/master") @section("content") @super Appended by index diff --git a/fixtures/layout-nested/index.json b/fixtures/layout-nested-compat/index.json similarity index 100% rename from fixtures/layout-nested/index.json rename to fixtures/layout-nested-compat/index.json diff --git a/fixtures/layout-nested/index.txt b/fixtures/layout-nested-compat/index.txt similarity index 100% rename from fixtures/layout-nested/index.txt rename to fixtures/layout-nested-compat/index.txt diff --git a/fixtures/layout-nested/master.edge b/fixtures/layout-nested-compat/master.edge similarity index 62% rename from fixtures/layout-nested/master.edge rename to fixtures/layout-nested-compat/master.edge index 9f77b36..edcc78a 100644 --- a/fixtures/layout-nested/master.edge +++ b/fixtures/layout-nested-compat/master.edge @@ -1,4 +1,4 @@ -@layout("layout-nested/super") +@layout("layout-nested-compat/super") @section("header") I will define the header myself @endsection diff --git a/fixtures/layout-nested/super.edge b/fixtures/layout-nested-compat/super.edge similarity index 100% rename from fixtures/layout-nested/super.edge rename to fixtures/layout-nested-compat/super.edge diff --git a/fixtures/layout-tag/compiled.js b/fixtures/layout-tag-compat/compiled.js similarity index 100% rename from fixtures/layout-tag/compiled.js rename to fixtures/layout-tag-compat/compiled.js diff --git a/fixtures/layout-tag-compat/index.edge b/fixtures/layout-tag-compat/index.edge new file mode 100644 index 0000000..f8e3440 --- /dev/null +++ b/fixtures/layout-tag-compat/index.edge @@ -0,0 +1 @@ +@layout("layout-tag-compat/master") \ No newline at end of file diff --git a/fixtures/layout-tag-extend-section/index.json b/fixtures/layout-tag-compat/index.json similarity index 100% rename from fixtures/layout-tag-extend-section/index.json rename to fixtures/layout-tag-compat/index.json diff --git a/fixtures/layout-tag/index.txt b/fixtures/layout-tag-compat/index.txt similarity index 100% rename from fixtures/layout-tag/index.txt rename to fixtures/layout-tag-compat/index.txt diff --git a/fixtures/layout-tag/master.edge b/fixtures/layout-tag-compat/master.edge similarity index 100% rename from fixtures/layout-tag/master.edge rename to fixtures/layout-tag-compat/master.edge diff --git a/fixtures/layout-tag-extend-section/compiled.js b/fixtures/layout-tag-extend-section-compat/compiled.js similarity index 100% rename from fixtures/layout-tag-extend-section/compiled.js rename to fixtures/layout-tag-extend-section-compat/compiled.js diff --git a/fixtures/layout-tag-extend-section/index.edge b/fixtures/layout-tag-extend-section-compat/index.edge similarity index 58% rename from fixtures/layout-tag-extend-section/index.edge rename to fixtures/layout-tag-extend-section-compat/index.edge index 74c884e..ff52d16 100644 --- a/fixtures/layout-tag-extend-section/index.edge +++ b/fixtures/layout-tag-extend-section-compat/index.edge @@ -1,4 +1,4 @@ -@layout("layout-tag-extend-section/master") +@layout("layout-tag-extend-section-compat/master") @section("content") @super Hello {{ username }} from children diff --git a/fixtures/layout-tag-multiple-sections/index.json b/fixtures/layout-tag-extend-section-compat/index.json similarity index 100% rename from fixtures/layout-tag-multiple-sections/index.json rename to fixtures/layout-tag-extend-section-compat/index.json diff --git a/fixtures/layout-tag-extend-section/index.txt b/fixtures/layout-tag-extend-section-compat/index.txt similarity index 100% rename from fixtures/layout-tag-extend-section/index.txt rename to fixtures/layout-tag-extend-section-compat/index.txt diff --git a/fixtures/layout-tag-extend-section/master.edge b/fixtures/layout-tag-extend-section-compat/master.edge similarity index 100% rename from fixtures/layout-tag-extend-section/master.edge rename to fixtures/layout-tag-extend-section-compat/master.edge diff --git a/fixtures/layout-tag-multiple-sections/compiled.js b/fixtures/layout-tag-multiple-sections-compat/compiled.js similarity index 100% rename from fixtures/layout-tag-multiple-sections/compiled.js rename to fixtures/layout-tag-multiple-sections-compat/compiled.js diff --git a/async-fixtures/layout-tag-multiple-sections/index.edge b/fixtures/layout-tag-multiple-sections-compat/index.edge similarity index 66% rename from async-fixtures/layout-tag-multiple-sections/index.edge rename to fixtures/layout-tag-multiple-sections-compat/index.edge index ddba73e..f8fa731 100644 --- a/async-fixtures/layout-tag-multiple-sections/index.edge +++ b/fixtures/layout-tag-multiple-sections-compat/index.edge @@ -1,4 +1,4 @@ -@layout("layout-tag-multiple-sections/master") +@layout("layout-tag-multiple-sections-compat/master") @section("content") I will override the content diff --git a/fixtures/layout-tag-override-sections/index.json b/fixtures/layout-tag-multiple-sections-compat/index.json similarity index 100% rename from fixtures/layout-tag-override-sections/index.json rename to fixtures/layout-tag-multiple-sections-compat/index.json diff --git a/fixtures/layout-tag-multiple-sections/index.txt b/fixtures/layout-tag-multiple-sections-compat/index.txt similarity index 100% rename from fixtures/layout-tag-multiple-sections/index.txt rename to fixtures/layout-tag-multiple-sections-compat/index.txt diff --git a/fixtures/layout-tag-multiple-sections/master.edge b/fixtures/layout-tag-multiple-sections-compat/master.edge similarity index 100% rename from fixtures/layout-tag-multiple-sections/master.edge rename to fixtures/layout-tag-multiple-sections-compat/master.edge diff --git a/fixtures/layout-tag-override-sections/compiled.js b/fixtures/layout-tag-override-sections-compat/compiled.js similarity index 100% rename from fixtures/layout-tag-override-sections/compiled.js rename to fixtures/layout-tag-override-sections-compat/compiled.js diff --git a/async-fixtures/layout-tag-override-sections/index.edge b/fixtures/layout-tag-override-sections-compat/index.edge similarity index 54% rename from async-fixtures/layout-tag-override-sections/index.edge rename to fixtures/layout-tag-override-sections-compat/index.edge index 8fa32cc..7aec0c0 100644 --- a/async-fixtures/layout-tag-override-sections/index.edge +++ b/fixtures/layout-tag-override-sections-compat/index.edge @@ -1,4 +1,4 @@ -@layout("layout-tag-override-sections/master") +@layout("layout-tag-override-sections-compat/master") @section("content") I will override the base content diff --git a/fixtures/layout-tag-sections/index.json b/fixtures/layout-tag-override-sections-compat/index.json similarity index 100% rename from fixtures/layout-tag-sections/index.json rename to fixtures/layout-tag-override-sections-compat/index.json diff --git a/fixtures/layout-tag-override-sections/index.txt b/fixtures/layout-tag-override-sections-compat/index.txt similarity index 100% rename from fixtures/layout-tag-override-sections/index.txt rename to fixtures/layout-tag-override-sections-compat/index.txt diff --git a/fixtures/layout-tag-override-sections/master.edge b/fixtures/layout-tag-override-sections-compat/master.edge similarity index 100% rename from fixtures/layout-tag-override-sections/master.edge rename to fixtures/layout-tag-override-sections-compat/master.edge diff --git a/fixtures/layout-tag-sections/compiled.js b/fixtures/layout-tag-sections-compat/compiled.js similarity index 100% rename from fixtures/layout-tag-sections/compiled.js rename to fixtures/layout-tag-sections-compat/compiled.js diff --git a/fixtures/layout-tag-sections-compat/index.edge b/fixtures/layout-tag-sections-compat/index.edge new file mode 100644 index 0000000..ae7bb82 --- /dev/null +++ b/fixtures/layout-tag-sections-compat/index.edge @@ -0,0 +1 @@ +@layout("layout-tag-sections-compat/master") \ No newline at end of file diff --git a/fixtures/layout-tag/index.json b/fixtures/layout-tag-sections-compat/index.json similarity index 100% rename from fixtures/layout-tag/index.json rename to fixtures/layout-tag-sections-compat/index.json diff --git a/fixtures/layout-tag-sections/index.txt b/fixtures/layout-tag-sections-compat/index.txt similarity index 100% rename from fixtures/layout-tag-sections/index.txt rename to fixtures/layout-tag-sections-compat/index.txt diff --git a/fixtures/layout-tag-sections/master.edge b/fixtures/layout-tag-sections-compat/master.edge similarity index 100% rename from fixtures/layout-tag-sections/master.edge rename to fixtures/layout-tag-sections-compat/master.edge diff --git a/fixtures/layout-tag-sections/index.edge b/fixtures/layout-tag-sections/index.edge deleted file mode 100644 index 0ebd12f..0000000 --- a/fixtures/layout-tag-sections/index.edge +++ /dev/null @@ -1 +0,0 @@ -@layout("layout-tag-sections/master") \ No newline at end of file diff --git a/fixtures/layout-tag/index.edge b/fixtures/layout-tag/index.edge deleted file mode 100644 index 807ff63..0000000 --- a/fixtures/layout-tag/index.edge +++ /dev/null @@ -1 +0,0 @@ -@layout("layout-tag/master") \ No newline at end of file diff --git a/fixtures/let-assign-with-each-scope/compiled.js b/fixtures/let-assign-with-each-scope/compiled.js new file mode 100644 index 0000000..cc77270 --- /dev/null +++ b/fixtures/let-assign-with-each-scope/compiled.js @@ -0,0 +1,24 @@ +let out = ""; +let $lineNumber = 1; +let $filename = "{{__dirname}}index.edge"; +try { +template.loop(state.users, function (user) { +out += "\n"; +$lineNumber = 2; +let index = 0; +$lineNumber = 3; +index = index + 1; +out += " "; +$lineNumber = 4; +out += `${template.escape(index)}`; +}); +$lineNumber = 6; +state.index = (state.index || 0) + 1; +out += ""; +out += "\n"; +$lineNumber = 8; +out += `${template.escape(state.index)}`; +} catch (error) { +template.reThrow(error, $filename, $lineNumber); +} +return out; \ No newline at end of file diff --git a/fixtures/let-assign-with-each-scope/index.edge b/fixtures/let-assign-with-each-scope/index.edge new file mode 100644 index 0000000..2892d48 --- /dev/null +++ b/fixtures/let-assign-with-each-scope/index.edge @@ -0,0 +1,8 @@ +@each(user in users) + @let(index = 0) + @assign(index = index + 1) + {{ index }} +@end +@assign(index = (index || 0) + 1) + +{{ index }} \ No newline at end of file diff --git a/fixtures/let-assign-with-each-scope/index.json b/fixtures/let-assign-with-each-scope/index.json new file mode 100644 index 0000000..9621a8b --- /dev/null +++ b/fixtures/let-assign-with-each-scope/index.json @@ -0,0 +1,3 @@ +{ + "users": ["virk"] +} \ No newline at end of file diff --git a/fixtures/let-assign-with-each-scope/index.txt b/fixtures/let-assign-with-each-scope/index.txt new file mode 100644 index 0000000..0bc999a --- /dev/null +++ b/fixtures/let-assign-with-each-scope/index.txt @@ -0,0 +1,2 @@ +1 +1 \ No newline at end of file diff --git a/fixtures/let-tag-array-destructure/compiled.js b/fixtures/let-tag-array-destructure/compiled.js new file mode 100644 index 0000000..90a29d9 --- /dev/null +++ b/fixtures/let-tag-array-destructure/compiled.js @@ -0,0 +1,13 @@ +let out = ""; +let $lineNumber = 1; +let $filename = "{{__dirname}}index.edge"; +try { +let [username, age] = state.user; +$lineNumber = 2; +out += `${template.escape(username)}`; +out += ", "; +out += `${template.escape(age)}`; +} catch (error) { +template.reThrow(error, $filename, $lineNumber); +} +return out; \ No newline at end of file diff --git a/fixtures/let-tag-array-destructure/index.edge b/fixtures/let-tag-array-destructure/index.edge new file mode 100644 index 0000000..17481ab --- /dev/null +++ b/fixtures/let-tag-array-destructure/index.edge @@ -0,0 +1,2 @@ +@let([username, age] = user) +{{ username }}, {{ age }} \ No newline at end of file diff --git a/fixtures/let-tag-array-destructure/index.json b/fixtures/let-tag-array-destructure/index.json new file mode 100644 index 0000000..50ea142 --- /dev/null +++ b/fixtures/let-tag-array-destructure/index.json @@ -0,0 +1,3 @@ +{ + "user": ["virk", 32] +} \ No newline at end of file diff --git a/fixtures/let-tag-array-destructure/index.txt b/fixtures/let-tag-array-destructure/index.txt new file mode 100644 index 0000000..1063d24 --- /dev/null +++ b/fixtures/let-tag-array-destructure/index.txt @@ -0,0 +1 @@ +virk, 32 \ No newline at end of file diff --git a/fixtures/let-tag-destructure-array-spread/compiled.js b/fixtures/let-tag-destructure-array-spread/compiled.js new file mode 100644 index 0000000..c3b61ef --- /dev/null +++ b/fixtures/let-tag-destructure-array-spread/compiled.js @@ -0,0 +1,15 @@ +let out = ""; +let $lineNumber = 1; +let $filename = "{{__dirname}}index.edge"; +try { +let [username, age, ...rest] = state.user; +$lineNumber = 2; +out += `${template.escape(username)}`; +out += ", "; +out += `${template.escape(age)}`; +out += " "; +out += `${template.escape(rest)}`; +} catch (error) { +template.reThrow(error, $filename, $lineNumber); +} +return out; \ No newline at end of file diff --git a/fixtures/let-tag-destructure-array-spread/index.edge b/fixtures/let-tag-destructure-array-spread/index.edge new file mode 100644 index 0000000..12a3a93 --- /dev/null +++ b/fixtures/let-tag-destructure-array-spread/index.edge @@ -0,0 +1,2 @@ +@let([username, age, ...rest] = user) +{{ username }}, {{ age }} {{ rest }} \ No newline at end of file diff --git a/fixtures/let-tag-destructure-array-spread/index.json b/fixtures/let-tag-destructure-array-spread/index.json new file mode 100644 index 0000000..50ea142 --- /dev/null +++ b/fixtures/let-tag-destructure-array-spread/index.json @@ -0,0 +1,3 @@ +{ + "user": ["virk", 32] +} \ No newline at end of file diff --git a/fixtures/let-tag-destructure-array-spread/index.txt b/fixtures/let-tag-destructure-array-spread/index.txt new file mode 100644 index 0000000..1063d24 --- /dev/null +++ b/fixtures/let-tag-destructure-array-spread/index.txt @@ -0,0 +1 @@ +virk, 32 \ No newline at end of file diff --git a/fixtures/let-tag-destructure-reassign/compiled.js b/fixtures/let-tag-destructure-reassign/compiled.js new file mode 100644 index 0000000..9c5d053 --- /dev/null +++ b/fixtures/let-tag-destructure-reassign/compiled.js @@ -0,0 +1,13 @@ +let out = ""; +let $lineNumber = 1; +let $filename = "{{__dirname}}index.edge"; +try { +let {username, age: userAge} = state.user; +$lineNumber = 2; +out += `${template.escape(username)}`; +out += ", "; +out += `${template.escape(userAge)}`; +} catch (error) { +template.reThrow(error, $filename, $lineNumber); +} +return out; \ No newline at end of file diff --git a/fixtures/let-tag-destructure-reassign/index.edge b/fixtures/let-tag-destructure-reassign/index.edge new file mode 100644 index 0000000..b4f7ee3 --- /dev/null +++ b/fixtures/let-tag-destructure-reassign/index.edge @@ -0,0 +1,2 @@ +@let({ username, age: userAge } = user) +{{ username }}, {{ userAge }} \ No newline at end of file diff --git a/fixtures/let-tag-destructure-reassign/index.json b/fixtures/let-tag-destructure-reassign/index.json new file mode 100644 index 0000000..552b024 --- /dev/null +++ b/fixtures/let-tag-destructure-reassign/index.json @@ -0,0 +1,6 @@ +{ + "user": { + "username": "virk", + "age": 32 + } +} \ No newline at end of file diff --git a/fixtures/let-tag-destructure-reassign/index.txt b/fixtures/let-tag-destructure-reassign/index.txt new file mode 100644 index 0000000..1063d24 --- /dev/null +++ b/fixtures/let-tag-destructure-reassign/index.txt @@ -0,0 +1 @@ +virk, 32 \ No newline at end of file diff --git a/fixtures/let-tag-destructure-spread/compiled.js b/fixtures/let-tag-destructure-spread/compiled.js new file mode 100644 index 0000000..92e5b4a --- /dev/null +++ b/fixtures/let-tag-destructure-spread/compiled.js @@ -0,0 +1,15 @@ +let out = ""; +let $lineNumber = 1; +let $filename = "{{__dirname}}index.edge"; +try { +let {username, age, ...rest} = state.user; +$lineNumber = 2; +out += `${template.escape(username)}`; +out += ", "; +out += `${template.escape(age)}`; +out += " "; +out += `${template.escape(rest)}`; +} catch (error) { +template.reThrow(error, $filename, $lineNumber); +} +return out; \ No newline at end of file diff --git a/fixtures/let-tag-destructure-spread/index.edge b/fixtures/let-tag-destructure-spread/index.edge new file mode 100644 index 0000000..b2ccc8f --- /dev/null +++ b/fixtures/let-tag-destructure-spread/index.edge @@ -0,0 +1,2 @@ +@let({ username, age, ...rest } = user) +{{ username }}, {{ age }} {{ rest }} \ No newline at end of file diff --git a/fixtures/let-tag-destructure-spread/index.json b/fixtures/let-tag-destructure-spread/index.json new file mode 100644 index 0000000..552b024 --- /dev/null +++ b/fixtures/let-tag-destructure-spread/index.json @@ -0,0 +1,6 @@ +{ + "user": { + "username": "virk", + "age": 32 + } +} \ No newline at end of file diff --git a/fixtures/let-tag-destructure-spread/index.txt b/fixtures/let-tag-destructure-spread/index.txt new file mode 100644 index 0000000..e6359d7 --- /dev/null +++ b/fixtures/let-tag-destructure-spread/index.txt @@ -0,0 +1 @@ +virk, 32 [object Object] \ No newline at end of file diff --git a/fixtures/let-tag-destructure/compiled.js b/fixtures/let-tag-destructure/compiled.js new file mode 100644 index 0000000..c7f738c --- /dev/null +++ b/fixtures/let-tag-destructure/compiled.js @@ -0,0 +1,13 @@ +let out = ""; +let $lineNumber = 1; +let $filename = "{{__dirname}}index.edge"; +try { +let {username, age} = state.user; +$lineNumber = 2; +out += `${template.escape(username)}`; +out += ", "; +out += `${template.escape(age)}`; +} catch (error) { +template.reThrow(error, $filename, $lineNumber); +} +return out; \ No newline at end of file diff --git a/fixtures/let-tag-destructure/index.edge b/fixtures/let-tag-destructure/index.edge new file mode 100644 index 0000000..b75498a --- /dev/null +++ b/fixtures/let-tag-destructure/index.edge @@ -0,0 +1,2 @@ +@let({ username, age } = user) +{{ username }}, {{ age }} \ No newline at end of file diff --git a/fixtures/let-tag-destructure/index.json b/fixtures/let-tag-destructure/index.json new file mode 100644 index 0000000..552b024 --- /dev/null +++ b/fixtures/let-tag-destructure/index.json @@ -0,0 +1,6 @@ +{ + "user": { + "username": "virk", + "age": 32 + } +} \ No newline at end of file diff --git a/fixtures/let-tag-destructure/index.txt b/fixtures/let-tag-destructure/index.txt new file mode 100644 index 0000000..1063d24 --- /dev/null +++ b/fixtures/let-tag-destructure/index.txt @@ -0,0 +1 @@ +virk, 32 \ No newline at end of file diff --git a/fixtures/let-tag-scope/compiled.js b/fixtures/let-tag-scope/compiled.js new file mode 100644 index 0000000..cc77270 --- /dev/null +++ b/fixtures/let-tag-scope/compiled.js @@ -0,0 +1,24 @@ +let out = ""; +let $lineNumber = 1; +let $filename = "{{__dirname}}index.edge"; +try { +template.loop(state.users, function (user) { +out += "\n"; +$lineNumber = 2; +let index = 0; +$lineNumber = 3; +index = index + 1; +out += " "; +$lineNumber = 4; +out += `${template.escape(index)}`; +}); +$lineNumber = 6; +state.index = (state.index || 0) + 1; +out += ""; +out += "\n"; +$lineNumber = 8; +out += `${template.escape(state.index)}`; +} catch (error) { +template.reThrow(error, $filename, $lineNumber); +} +return out; \ No newline at end of file diff --git a/fixtures/let-tag-scope/index.edge b/fixtures/let-tag-scope/index.edge new file mode 100644 index 0000000..e6d0684 --- /dev/null +++ b/fixtures/let-tag-scope/index.edge @@ -0,0 +1,8 @@ +@each(user in users) + @let(index = 0) + @eval(index = index + 1) + {{ index }} +@end +@eval(index = (index || 0) + 1) + +{{ index }} \ No newline at end of file diff --git a/fixtures/let-tag-scope/index.json b/fixtures/let-tag-scope/index.json new file mode 100644 index 0000000..9621a8b --- /dev/null +++ b/fixtures/let-tag-scope/index.json @@ -0,0 +1,3 @@ +{ + "users": ["virk"] +} \ No newline at end of file diff --git a/fixtures/let-tag-scope/index.txt b/fixtures/let-tag-scope/index.txt new file mode 100644 index 0000000..0bc999a --- /dev/null +++ b/fixtures/let-tag-scope/index.txt @@ -0,0 +1,2 @@ +1 +1 \ No newline at end of file diff --git a/fixtures/let-tag/compiled.js b/fixtures/let-tag/compiled.js new file mode 100644 index 0000000..7f2856c --- /dev/null +++ b/fixtures/let-tag/compiled.js @@ -0,0 +1,11 @@ +let out = ""; +let $lineNumber = 1; +let $filename = "{{__dirname}}index.edge"; +try { +let username = 'nikk'; +$lineNumber = 2; +out += `${template.escape(username)}`; +} catch (error) { +template.reThrow(error, $filename, $lineNumber); +} +return out; \ No newline at end of file diff --git a/fixtures/let-tag/index.edge b/fixtures/let-tag/index.edge new file mode 100644 index 0000000..cd54d51 --- /dev/null +++ b/fixtures/let-tag/index.edge @@ -0,0 +1,2 @@ +@let(username = 'nikk') +{{ username }} \ No newline at end of file diff --git a/fixtures/set-mutate-collection/index.json b/fixtures/let-tag/index.json similarity index 100% rename from fixtures/set-mutate-collection/index.json rename to fixtures/let-tag/index.json diff --git a/fixtures/set-tag/index.txt b/fixtures/let-tag/index.txt similarity index 100% rename from fixtures/set-tag/index.txt rename to fixtures/let-tag/index.txt diff --git a/fixtures/set-mutate-collection/compiled.js b/fixtures/set-mutate-collection-compat/compiled.js similarity index 100% rename from fixtures/set-mutate-collection/compiled.js rename to fixtures/set-mutate-collection-compat/compiled.js diff --git a/fixtures/set-mutate-collection/index.edge b/fixtures/set-mutate-collection-compat/index.edge similarity index 100% rename from fixtures/set-mutate-collection/index.edge rename to fixtures/set-mutate-collection-compat/index.edge diff --git a/fixtures/set-tag-identifier/index.json b/fixtures/set-mutate-collection-compat/index.json similarity index 100% rename from fixtures/set-tag-identifier/index.json rename to fixtures/set-mutate-collection-compat/index.json diff --git a/fixtures/set-mutate-collection/index.txt b/fixtures/set-mutate-collection-compat/index.txt similarity index 100% rename from fixtures/set-mutate-collection/index.txt rename to fixtures/set-mutate-collection-compat/index.txt diff --git a/fixtures/set-tag/compiled.js b/fixtures/set-tag-compat/compiled.js similarity index 100% rename from fixtures/set-tag/compiled.js rename to fixtures/set-tag-compat/compiled.js diff --git a/fixtures/set-tag/index.edge b/fixtures/set-tag-compat/index.edge similarity index 100% rename from fixtures/set-tag/index.edge rename to fixtures/set-tag-compat/index.edge diff --git a/fixtures/set-tag-update-value/index.json b/fixtures/set-tag-compat/index.json similarity index 100% rename from fixtures/set-tag-update-value/index.json rename to fixtures/set-tag-compat/index.json diff --git a/fixtures/set-tag-compat/index.txt b/fixtures/set-tag-compat/index.txt new file mode 100644 index 0000000..c12829d --- /dev/null +++ b/fixtures/set-tag-compat/index.txt @@ -0,0 +1 @@ +nikk \ No newline at end of file diff --git a/fixtures/set-tag-identifier/compiled.js b/fixtures/set-tag-identifier-compat/compiled.js similarity index 100% rename from fixtures/set-tag-identifier/compiled.js rename to fixtures/set-tag-identifier-compat/compiled.js diff --git a/fixtures/set-tag-identifier/index.edge b/fixtures/set-tag-identifier-compat/index.edge similarity index 100% rename from fixtures/set-tag-identifier/index.edge rename to fixtures/set-tag-identifier-compat/index.edge diff --git a/fixtures/set-tag/index.json b/fixtures/set-tag-identifier-compat/index.json similarity index 100% rename from fixtures/set-tag/index.json rename to fixtures/set-tag-identifier-compat/index.json diff --git a/fixtures/set-tag-identifier/index.txt b/fixtures/set-tag-identifier-compat/index.txt similarity index 100% rename from fixtures/set-tag-identifier/index.txt rename to fixtures/set-tag-identifier-compat/index.txt diff --git a/fixtures/set-tag-update-value/compiled.js b/fixtures/set-tag-update-value-compat/compiled.js similarity index 100% rename from fixtures/set-tag-update-value/compiled.js rename to fixtures/set-tag-update-value-compat/compiled.js diff --git a/fixtures/set-tag-update-value/index.edge b/fixtures/set-tag-update-value-compat/index.edge similarity index 100% rename from fixtures/set-tag-update-value/index.edge rename to fixtures/set-tag-update-value-compat/index.edge diff --git a/newline-fixtures/component-custom-slots/index.json b/fixtures/set-tag-update-value-compat/index.json similarity index 100% rename from newline-fixtures/component-custom-slots/index.json rename to fixtures/set-tag-update-value-compat/index.json diff --git a/fixtures/set-tag-update-value/index.txt b/fixtures/set-tag-update-value-compat/index.txt similarity index 100% rename from fixtures/set-tag-update-value/index.txt rename to fixtures/set-tag-update-value-compat/index.txt diff --git a/index.ts b/index.ts index 3b1deaf..38d5aff 100644 --- a/index.ts +++ b/index.ts @@ -1,22 +1,17 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -export * from './src/Contracts' -import { Edge } from './src/Edge' -import { safeValue } from './src/Template' -import { GLOBALS } from './src/Edge/globals' +import { Edge } from './src/edge/main.js' -/** - * Default export - */ -const edge = new Edge() -Object.keys(GLOBALS).forEach((key) => edge.global(key, GLOBALS[key])) -export default edge +export { Template } from './src/template.js' +export { edgeGlobals } from './src/edge/globals.js' +export { Edge } -export { Edge, safeValue, GLOBALS } +const edge = Edge.create() +export default edge diff --git a/newline-fixtures/comments-multiline-prefix/index.edge b/newline_fixtures/comments-multiline-prefix/index.edge similarity index 100% rename from newline-fixtures/comments-multiline-prefix/index.edge rename to newline_fixtures/comments-multiline-prefix/index.edge diff --git a/newline-fixtures/comments-multiline/index.json b/newline_fixtures/comments-multiline-prefix/index.json similarity index 100% rename from newline-fixtures/comments-multiline/index.json rename to newline_fixtures/comments-multiline-prefix/index.json diff --git a/newline-fixtures/comments-multiline-prefix/index.txt b/newline_fixtures/comments-multiline-prefix/index.txt similarity index 100% rename from newline-fixtures/comments-multiline-prefix/index.txt rename to newline_fixtures/comments-multiline-prefix/index.txt diff --git a/newline-fixtures/comments-multiline-suffix/index.edge b/newline_fixtures/comments-multiline-suffix/index.edge similarity index 100% rename from newline-fixtures/comments-multiline-suffix/index.edge rename to newline_fixtures/comments-multiline-suffix/index.edge diff --git a/newline-fixtures/comments-prefix/index.json b/newline_fixtures/comments-multiline-suffix/index.json similarity index 100% rename from newline-fixtures/comments-prefix/index.json rename to newline_fixtures/comments-multiline-suffix/index.json diff --git a/newline-fixtures/comments-multiline-suffix/index.txt b/newline_fixtures/comments-multiline-suffix/index.txt similarity index 100% rename from newline-fixtures/comments-multiline-suffix/index.txt rename to newline_fixtures/comments-multiline-suffix/index.txt diff --git a/newline-fixtures/comments-multiline/index.edge b/newline_fixtures/comments-multiline/index.edge similarity index 100% rename from newline-fixtures/comments-multiline/index.edge rename to newline_fixtures/comments-multiline/index.edge diff --git a/newline-fixtures/comments-suffix/index.json b/newline_fixtures/comments-multiline/index.json similarity index 100% rename from newline-fixtures/comments-suffix/index.json rename to newline_fixtures/comments-multiline/index.json diff --git a/newline-fixtures/comments-multiline/index.txt b/newline_fixtures/comments-multiline/index.txt similarity index 100% rename from newline-fixtures/comments-multiline/index.txt rename to newline_fixtures/comments-multiline/index.txt diff --git a/newline-fixtures/comments-prefix/index.edge b/newline_fixtures/comments-prefix/index.edge similarity index 100% rename from newline-fixtures/comments-prefix/index.edge rename to newline_fixtures/comments-prefix/index.edge diff --git a/newline-fixtures/comments/index.json b/newline_fixtures/comments-prefix/index.json similarity index 100% rename from newline-fixtures/comments/index.json rename to newline_fixtures/comments-prefix/index.json diff --git a/newline-fixtures/comments-prefix/index.txt b/newline_fixtures/comments-prefix/index.txt similarity index 100% rename from newline-fixtures/comments-prefix/index.txt rename to newline_fixtures/comments-prefix/index.txt diff --git a/newline-fixtures/comments-suffix/index.edge b/newline_fixtures/comments-suffix/index.edge similarity index 100% rename from newline-fixtures/comments-suffix/index.edge rename to newline_fixtures/comments-suffix/index.edge diff --git a/newline_fixtures/comments-suffix/index.json b/newline_fixtures/comments-suffix/index.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/newline_fixtures/comments-suffix/index.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/newline-fixtures/comments-suffix/index.txt b/newline_fixtures/comments-suffix/index.txt similarity index 100% rename from newline-fixtures/comments-suffix/index.txt rename to newline_fixtures/comments-suffix/index.txt diff --git a/newline-fixtures/comments/index.edge b/newline_fixtures/comments/index.edge similarity index 100% rename from newline-fixtures/comments/index.edge rename to newline_fixtures/comments/index.edge diff --git a/newline_fixtures/comments/index.json b/newline_fixtures/comments/index.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/newline_fixtures/comments/index.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/newline-fixtures/comments/index.txt b/newline_fixtures/comments/index.txt similarity index 100% rename from newline-fixtures/comments/index.txt rename to newline_fixtures/comments/index.txt diff --git a/newline-fixtures/component-custom-slots/alert.edge b/newline_fixtures/component-custom-slots/alert.edge similarity index 100% rename from newline-fixtures/component-custom-slots/alert.edge rename to newline_fixtures/component-custom-slots/alert.edge diff --git a/newline-fixtures/component-custom-slots/index.edge b/newline_fixtures/component-custom-slots/index.edge similarity index 100% rename from newline-fixtures/component-custom-slots/index.edge rename to newline_fixtures/component-custom-slots/index.edge diff --git a/newline-fixtures/component-with-bottom-content/index.json b/newline_fixtures/component-custom-slots/index.json similarity index 100% rename from newline-fixtures/component-with-bottom-content/index.json rename to newline_fixtures/component-custom-slots/index.json diff --git a/newline-fixtures/component-custom-slots/index.txt b/newline_fixtures/component-custom-slots/index.txt similarity index 100% rename from newline-fixtures/component-custom-slots/index.txt rename to newline_fixtures/component-custom-slots/index.txt diff --git a/newline-fixtures/component-with-bottom-content/alert.edge b/newline_fixtures/component-with-bottom-content/alert.edge similarity index 100% rename from newline-fixtures/component-with-bottom-content/alert.edge rename to newline_fixtures/component-with-bottom-content/alert.edge diff --git a/newline-fixtures/component-with-bottom-content/index.edge b/newline_fixtures/component-with-bottom-content/index.edge similarity index 100% rename from newline-fixtures/component-with-bottom-content/index.edge rename to newline_fixtures/component-with-bottom-content/index.edge diff --git a/newline-fixtures/component-with-top-content/index.json b/newline_fixtures/component-with-bottom-content/index.json similarity index 100% rename from newline-fixtures/component-with-top-content/index.json rename to newline_fixtures/component-with-bottom-content/index.json diff --git a/newline-fixtures/component-with-bottom-content/index.txt b/newline_fixtures/component-with-bottom-content/index.txt similarity index 100% rename from newline-fixtures/component-with-bottom-content/index.txt rename to newline_fixtures/component-with-bottom-content/index.txt diff --git a/newline-fixtures/component-with-top-content/alert.edge b/newline_fixtures/component-with-top-content/alert.edge similarity index 100% rename from newline-fixtures/component-with-top-content/alert.edge rename to newline_fixtures/component-with-top-content/alert.edge diff --git a/newline-fixtures/component-with-top-content/index.edge b/newline_fixtures/component-with-top-content/index.edge similarity index 100% rename from newline-fixtures/component-with-top-content/index.edge rename to newline_fixtures/component-with-top-content/index.edge diff --git a/newline-fixtures/component/index.json b/newline_fixtures/component-with-top-content/index.json similarity index 100% rename from newline-fixtures/component/index.json rename to newline_fixtures/component-with-top-content/index.json diff --git a/newline-fixtures/component-with-top-content/index.txt b/newline_fixtures/component-with-top-content/index.txt similarity index 100% rename from newline-fixtures/component-with-top-content/index.txt rename to newline_fixtures/component-with-top-content/index.txt diff --git a/newline-fixtures/component/alert.edge b/newline_fixtures/component/alert.edge similarity index 100% rename from newline-fixtures/component/alert.edge rename to newline_fixtures/component/alert.edge diff --git a/newline-fixtures/component/index.edge b/newline_fixtures/component/index.edge similarity index 100% rename from newline-fixtures/component/index.edge rename to newline_fixtures/component/index.edge diff --git a/newline_fixtures/component/index.json b/newline_fixtures/component/index.json new file mode 100644 index 0000000..d5f5540 --- /dev/null +++ b/newline_fixtures/component/index.json @@ -0,0 +1,3 @@ +{ + "username": "virk" +} \ No newline at end of file diff --git a/newline-fixtures/component/index.txt b/newline_fixtures/component/index.txt similarity index 100% rename from newline-fixtures/component/index.txt rename to newline_fixtures/component/index.txt diff --git a/newline-fixtures/each-with-bottom-content/index.edge b/newline_fixtures/each-with-bottom-content/index.edge similarity index 100% rename from newline-fixtures/each-with-bottom-content/index.edge rename to newline_fixtures/each-with-bottom-content/index.edge diff --git a/newline-fixtures/each-with-bottom-content/index.json b/newline_fixtures/each-with-bottom-content/index.json similarity index 100% rename from newline-fixtures/each-with-bottom-content/index.json rename to newline_fixtures/each-with-bottom-content/index.json diff --git a/newline-fixtures/each-with-bottom-content/index.txt b/newline_fixtures/each-with-bottom-content/index.txt similarity index 100% rename from newline-fixtures/each-with-bottom-content/index.txt rename to newline_fixtures/each-with-bottom-content/index.txt diff --git a/newline-fixtures/each-with-sorrounding-content/index.edge b/newline_fixtures/each-with-sorrounding-content/index.edge similarity index 100% rename from newline-fixtures/each-with-sorrounding-content/index.edge rename to newline_fixtures/each-with-sorrounding-content/index.edge diff --git a/newline-fixtures/each-with-sorrounding-content/index.json b/newline_fixtures/each-with-sorrounding-content/index.json similarity index 100% rename from newline-fixtures/each-with-sorrounding-content/index.json rename to newline_fixtures/each-with-sorrounding-content/index.json diff --git a/newline-fixtures/each-with-sorrounding-content/index.txt b/newline_fixtures/each-with-sorrounding-content/index.txt similarity index 100% rename from newline-fixtures/each-with-sorrounding-content/index.txt rename to newline_fixtures/each-with-sorrounding-content/index.txt diff --git a/newline-fixtures/each-with-top-content/index.edge b/newline_fixtures/each-with-top-content/index.edge similarity index 100% rename from newline-fixtures/each-with-top-content/index.edge rename to newline_fixtures/each-with-top-content/index.edge diff --git a/newline-fixtures/each-with-top-content/index.json b/newline_fixtures/each-with-top-content/index.json similarity index 100% rename from newline-fixtures/each-with-top-content/index.json rename to newline_fixtures/each-with-top-content/index.json diff --git a/newline-fixtures/each-with-top-content/index.txt b/newline_fixtures/each-with-top-content/index.txt similarity index 100% rename from newline-fixtures/each-with-top-content/index.txt rename to newline_fixtures/each-with-top-content/index.txt diff --git a/newline-fixtures/each/index.edge b/newline_fixtures/each/index.edge similarity index 100% rename from newline-fixtures/each/index.edge rename to newline_fixtures/each/index.edge diff --git a/newline-fixtures/each/index.json b/newline_fixtures/each/index.json similarity index 100% rename from newline-fixtures/each/index.json rename to newline_fixtures/each/index.json diff --git a/newline-fixtures/each/index.txt b/newline_fixtures/each/index.txt similarity index 100% rename from newline-fixtures/each/index.txt rename to newline_fixtures/each/index.txt diff --git a/newline-fixtures/else-with-bottom-content/index.edge b/newline_fixtures/else-with-bottom-content/index.edge similarity index 100% rename from newline-fixtures/else-with-bottom-content/index.edge rename to newline_fixtures/else-with-bottom-content/index.edge diff --git a/newline-fixtures/else-with-bottom-content/index.json b/newline_fixtures/else-with-bottom-content/index.json similarity index 100% rename from newline-fixtures/else-with-bottom-content/index.json rename to newline_fixtures/else-with-bottom-content/index.json diff --git a/newline-fixtures/else-with-bottom-content/index.txt b/newline_fixtures/else-with-bottom-content/index.txt similarity index 100% rename from newline-fixtures/else-with-bottom-content/index.txt rename to newline_fixtures/else-with-bottom-content/index.txt diff --git a/newline-fixtures/else-with-top-content/index.edge b/newline_fixtures/else-with-top-content/index.edge similarity index 100% rename from newline-fixtures/else-with-top-content/index.edge rename to newline_fixtures/else-with-top-content/index.edge diff --git a/newline-fixtures/else-with-top-content/index.json b/newline_fixtures/else-with-top-content/index.json similarity index 100% rename from newline-fixtures/else-with-top-content/index.json rename to newline_fixtures/else-with-top-content/index.json diff --git a/newline-fixtures/else-with-top-content/index.txt b/newline_fixtures/else-with-top-content/index.txt similarity index 100% rename from newline-fixtures/else-with-top-content/index.txt rename to newline_fixtures/else-with-top-content/index.txt diff --git a/newline-fixtures/else/index.edge b/newline_fixtures/else/index.edge similarity index 100% rename from newline-fixtures/else/index.edge rename to newline_fixtures/else/index.edge diff --git a/newline-fixtures/else/index.json b/newline_fixtures/else/index.json similarity index 100% rename from newline-fixtures/else/index.json rename to newline_fixtures/else/index.json diff --git a/newline-fixtures/else/index.txt b/newline_fixtures/else/index.txt similarity index 100% rename from newline-fixtures/else/index.txt rename to newline_fixtures/else/index.txt diff --git a/newline-fixtures/if-with-bottom-content/index.edge b/newline_fixtures/if-with-bottom-content/index.edge similarity index 100% rename from newline-fixtures/if-with-bottom-content/index.edge rename to newline_fixtures/if-with-bottom-content/index.edge diff --git a/newline-fixtures/if-with-bottom-content/index.json b/newline_fixtures/if-with-bottom-content/index.json similarity index 100% rename from newline-fixtures/if-with-bottom-content/index.json rename to newline_fixtures/if-with-bottom-content/index.json diff --git a/newline-fixtures/if-with-bottom-content/index.txt b/newline_fixtures/if-with-bottom-content/index.txt similarity index 100% rename from newline-fixtures/if-with-bottom-content/index.txt rename to newline_fixtures/if-with-bottom-content/index.txt diff --git a/newline-fixtures/if-with-top-content/index.edge b/newline_fixtures/if-with-top-content/index.edge similarity index 100% rename from newline-fixtures/if-with-top-content/index.edge rename to newline_fixtures/if-with-top-content/index.edge diff --git a/newline-fixtures/if-with-top-content/index.json b/newline_fixtures/if-with-top-content/index.json similarity index 100% rename from newline-fixtures/if-with-top-content/index.json rename to newline_fixtures/if-with-top-content/index.json diff --git a/newline-fixtures/if-with-top-content/index.txt b/newline_fixtures/if-with-top-content/index.txt similarity index 100% rename from newline-fixtures/if-with-top-content/index.txt rename to newline_fixtures/if-with-top-content/index.txt diff --git a/newline-fixtures/if/index.edge b/newline_fixtures/if/index.edge similarity index 100% rename from newline-fixtures/if/index.edge rename to newline_fixtures/if/index.edge diff --git a/newline-fixtures/if/index.json b/newline_fixtures/if/index.json similarity index 100% rename from newline-fixtures/if/index.json rename to newline_fixtures/if/index.json diff --git a/newline-fixtures/if/index.txt b/newline_fixtures/if/index.txt similarity index 100% rename from newline-fixtures/if/index.txt rename to newline_fixtures/if/index.txt diff --git a/newline-fixtures/layouts/index.edge b/newline_fixtures/layouts-compat/index.edge similarity index 57% rename from newline-fixtures/layouts/index.edge rename to newline_fixtures/layouts-compat/index.edge index 2f1de9f..57f7747 100644 --- a/newline-fixtures/layouts/index.edge +++ b/newline_fixtures/layouts-compat/index.edge @@ -1,4 +1,4 @@ -@layout('layouts/master') +@layout('layouts-compat/master') @section('content') Hello world diff --git a/newline-fixtures/layouts-with-multiple-sections/index.json b/newline_fixtures/layouts-compat/index.json similarity index 100% rename from newline-fixtures/layouts-with-multiple-sections/index.json rename to newline_fixtures/layouts-compat/index.json diff --git a/newline-fixtures/layouts/index.txt b/newline_fixtures/layouts-compat/index.txt similarity index 100% rename from newline-fixtures/layouts/index.txt rename to newline_fixtures/layouts-compat/index.txt diff --git a/newline-fixtures/layouts/master.edge b/newline_fixtures/layouts-compat/master.edge similarity index 100% rename from newline-fixtures/layouts/master.edge rename to newline_fixtures/layouts-compat/master.edge diff --git a/newline-fixtures/layouts-with-multiple-sections/index.edge b/newline_fixtures/layouts-with-multiple-sections-compat/index.edge similarity index 66% rename from newline-fixtures/layouts-with-multiple-sections/index.edge rename to newline_fixtures/layouts-with-multiple-sections-compat/index.edge index cf0034a..77626d9 100644 --- a/newline-fixtures/layouts-with-multiple-sections/index.edge +++ b/newline_fixtures/layouts-with-multiple-sections-compat/index.edge @@ -1,4 +1,4 @@ -@layout('layouts-with-multiple-sections/master') +@layout('layouts-with-multiple-sections-compat/master') @set('username', 'virk') diff --git a/newline-fixtures/layouts/index.json b/newline_fixtures/layouts-with-multiple-sections-compat/index.json similarity index 100% rename from newline-fixtures/layouts/index.json rename to newline_fixtures/layouts-with-multiple-sections-compat/index.json diff --git a/newline-fixtures/layouts-with-multiple-sections/index.txt b/newline_fixtures/layouts-with-multiple-sections-compat/index.txt similarity index 100% rename from newline-fixtures/layouts-with-multiple-sections/index.txt rename to newline_fixtures/layouts-with-multiple-sections-compat/index.txt diff --git a/newline-fixtures/layouts-with-multiple-sections/master.edge b/newline_fixtures/layouts-with-multiple-sections-compat/master.edge similarity index 100% rename from newline-fixtures/layouts-with-multiple-sections/master.edge rename to newline_fixtures/layouts-with-multiple-sections-compat/master.edge diff --git a/newline-fixtures/set/index.edge b/newline_fixtures/set/index.edge similarity index 100% rename from newline-fixtures/set/index.edge rename to newline_fixtures/set/index.edge diff --git a/newline-fixtures/set/index.json b/newline_fixtures/set/index.json similarity index 100% rename from newline-fixtures/set/index.json rename to newline_fixtures/set/index.json diff --git a/newline-fixtures/set/index.txt b/newline_fixtures/set/index.txt similarity index 100% rename from newline-fixtures/set/index.txt rename to newline_fixtures/set/index.txt diff --git a/newline-fixtures/yaml-components-with-slots/index.edge b/newline_fixtures/yaml-components-with-slots/index.edge similarity index 75% rename from newline-fixtures/yaml-components-with-slots/index.edge rename to newline_fixtures/yaml-components-with-slots/index.edge index 3e3a66e..fd7cf21 100644 --- a/newline-fixtures/yaml-components-with-slots/index.edge +++ b/newline_fixtures/yaml-components-with-slots/index.edge @@ -4,7 +4,7 @@ name: {{ user.name }} job: {{ user.job }} skills: - @component('yaml-components/skills', skills = user.skills) + @component('yaml-components/skills', { skills: user.skills }) @slot('skill', skill) - {{ skill }} @endslot diff --git a/newline-fixtures/yaml-components-with-slots/index.json b/newline_fixtures/yaml-components-with-slots/index.json similarity index 100% rename from newline-fixtures/yaml-components-with-slots/index.json rename to newline_fixtures/yaml-components-with-slots/index.json diff --git a/newline-fixtures/yaml-components-with-slots/index.txt b/newline_fixtures/yaml-components-with-slots/index.txt similarity index 100% rename from newline-fixtures/yaml-components-with-slots/index.txt rename to newline_fixtures/yaml-components-with-slots/index.txt diff --git a/newline-fixtures/yaml-components-with-slots/skills.edge b/newline_fixtures/yaml-components-with-slots/skills.edge similarity index 100% rename from newline-fixtures/yaml-components-with-slots/skills.edge rename to newline_fixtures/yaml-components-with-slots/skills.edge diff --git a/newline-fixtures/yaml-components/index.edge b/newline_fixtures/yaml-components/index.edge similarity index 67% rename from newline-fixtures/yaml-components/index.edge rename to newline_fixtures/yaml-components/index.edge index 2f36626..3e0dda9 100644 --- a/newline-fixtures/yaml-components/index.edge +++ b/newline_fixtures/yaml-components/index.edge @@ -4,6 +4,6 @@ name: {{ user.name }} job: {{ user.job }} skills: - @component('yaml-components/skills', skills = user.skills) + @component('yaml-components/skills', { skills: user.skills }) @endcomponent @endeach \ No newline at end of file diff --git a/newline-fixtures/yaml-components/index.json b/newline_fixtures/yaml-components/index.json similarity index 100% rename from newline-fixtures/yaml-components/index.json rename to newline_fixtures/yaml-components/index.json diff --git a/newline-fixtures/yaml-components/index.txt b/newline_fixtures/yaml-components/index.txt similarity index 100% rename from newline-fixtures/yaml-components/index.txt rename to newline_fixtures/yaml-components/index.txt diff --git a/newline-fixtures/yaml-components/skills.edge b/newline_fixtures/yaml-components/skills.edge similarity index 100% rename from newline-fixtures/yaml-components/skills.edge rename to newline_fixtures/yaml-components/skills.edge diff --git a/newline-fixtures/yaml-partials/index.edge b/newline_fixtures/yaml-partials/index.edge similarity index 100% rename from newline-fixtures/yaml-partials/index.edge rename to newline_fixtures/yaml-partials/index.edge diff --git a/newline-fixtures/yaml-partials/index.json b/newline_fixtures/yaml-partials/index.json similarity index 100% rename from newline-fixtures/yaml-partials/index.json rename to newline_fixtures/yaml-partials/index.json diff --git a/newline-fixtures/yaml-partials/index.txt b/newline_fixtures/yaml-partials/index.txt similarity index 100% rename from newline-fixtures/yaml-partials/index.txt rename to newline_fixtures/yaml-partials/index.txt diff --git a/newline-fixtures/yaml-partials/skills.edge b/newline_fixtures/yaml-partials/skills.edge similarity index 100% rename from newline-fixtures/yaml-partials/skills.edge rename to newline_fixtures/yaml-partials/skills.edge diff --git a/newline-fixtures/yaml/index.edge b/newline_fixtures/yaml/index.edge similarity index 100% rename from newline-fixtures/yaml/index.edge rename to newline_fixtures/yaml/index.edge diff --git a/newline-fixtures/yaml/index.json b/newline_fixtures/yaml/index.json similarity index 100% rename from newline-fixtures/yaml/index.json rename to newline_fixtures/yaml/index.json diff --git a/newline-fixtures/yaml/index.txt b/newline_fixtures/yaml/index.txt similarity index 100% rename from newline-fixtures/yaml/index.txt rename to newline_fixtures/yaml/index.txt diff --git a/package.json b/package.json index e3cb2c7..4d0bfe8 100644 --- a/package.json +++ b/package.json @@ -1,91 +1,89 @@ { "name": "edge.js", - "version": "5.5.1", "description": "Template engine", + "version": "6.0.0-10", + "engines": { + "node": ">=18.16.0" + }, "main": "build/index.js", + "type": "module", "files": [ - "build/src", - "build/index.d.ts", - "build/index.js" + "build", + "!build/bin", + "!build/examples", + "!build/tests", + "!build/tests_helpers" ], + "exports": { + ".": "./build/index.js", + "./types": "./build/src/types.js", + "./plugins/migrate": "./build/src/migrate/plugin.js" + }, "scripts": { "pretest": "npm run lint", - "test": "node -r @adonisjs/require-ts/build/register ./bin/test.ts", - "mrm": "mrm --preset=@adonisjs/mrm-preset", - "commit": "git-cz", + "test": "c8 npm run quick:test", "clean": "del-cli build", - "compile": "npm run lint && npm run clean && tsc", + "typecheck": "tsc --noEmit", + "precompile": "npm run lint && npm run clean", + "compile": "tsup-node && tsc --emitDeclarationOnly --declaration", "build": "npm run compile", + "prepublishOnly": "npm run build", "lint": "eslint . --ext=.ts", - "release": "np --message=\"chore(release): %s\"", - "version": "npm run build", "format": "prettier --write .", - "sync-labels": "github-label-sync --labels ./node_modules/@adonisjs/mrm-preset/gh-labels.json edge-js/edge", - "prepublishOnly": "npm run build" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/poppinss/edge.git" - }, - "author": "virk", - "license": "MIT", - "bugs": { - "url": "https://github.com/poppinss/edge/issues" + "release": "np", + "version": "npm run build", + "sync-labels": "github-label-sync --labels .github/labels.json edge-js/edge", + "quick:test": "node --enable-source-maps --loader=ts-node/esm bin/test.ts" }, - "homepage": "https://github.com/poppinss/edge#readme", "devDependencies": { - "@adonisjs/mrm-preset": "^5.0.3", - "@adonisjs/require-ts": "^2.0.12", - "@japa/assert": "^1.3.4", - "@japa/run-failed-tests": "^1.0.7", - "@japa/runner": "^2.0.9", - "@japa/spec-reporter": "^1.1.12", + "@adonisjs/eslint-config": "^1.1.8", + "@adonisjs/prettier-config": "^1.1.8", + "@adonisjs/tsconfig": "^1.1.8", + "@commitlint/cli": "^18.2.0", + "@commitlint/config-conventional": "^18.1.0", + "@japa/assert": "^2.0.1", + "@japa/file-system": "^2.0.1", + "@japa/runner": "^3.0.5", "@poppinss/dev-utils": "^2.0.3", - "@types/node": "^18.7.14", - "chai": "^4.3.6", - "commitizen": "^4.2.5", - "cz-conventional-changelog": "^3.3.0", + "@swc/core": "^1.3.96", + "@types/fs-readdir-recursive": "^1.1.1", + "@types/he": "^1.2.2", + "@types/node": "^20.8.10", + "c8": "^8.0.0", "dedent-js": "^1.0.1", - "del-cli": "^5.0.0", - "doctoc": "^2.2.0", - "eslint": "^8.23.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-adonis": "^2.1.0", - "eslint-plugin-prettier": "^4.2.1", + "del-cli": "^5.1.0", + "eslint": "^8.53.0", "github-label-sync": "^2.2.0", - "husky": "^8.0.1", - "mrm": "^4.1.0", - "np": "^7.6.2", - "prettier": "^2.7.1", - "typescript": "^5.0.2" - }, - "config": { - "commitizen": { - "path": "cz-conventional-changelog" - } - }, - "nyc": { - "exclude": [ - "test" - ], - "extension": [ - ".ts" - ] + "husky": "^8.0.3", + "np": "^8.0.4", + "prettier": "^3.0.3", + "ts-node": "^10.9.1", + "tsup": "^7.1.0", + "typescript": "^5.2.2" }, "dependencies": { "@poppinss/inspect": "^1.0.1", - "@poppinss/utils": "^5.0.0", - "edge-error": "^3.0.0", - "edge-lexer": "^5.0.2", - "edge-parser": "^8.2.1", + "@poppinss/macroable": "^1.0.0", + "@poppinss/utils": "^6.5.1", + "classnames": "^2.3.2", + "edge-error": "^4.0.0", + "edge-lexer": "^6.0.0", + "edge-parser": "^9.0.0", + "fs-readdir-recursive": "^1.1.0", + "he": "^1.2.0", "js-stringify": "^1.0.2", - "macroable": "^7.0.1", - "stringify-attributes": "^2.0.0" + "property-information": "^6.4.0", + "stringify-attributes": "^4.0.0" }, - "husky": { - "hooks": { - "commit-msg": "node ./node_modules/@adonisjs/mrm-preset/validateCommit/conventional/validate.js" - } + "author": "virk", + "license": "MIT", + "homepage": "https://github.com/poppinss/edge#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/poppinss/edge.git" + }, + "bugs": { + "url": "https://github.com/poppinss/edge/issues" }, "directories": { "example": "examples", @@ -96,55 +94,45 @@ "mustache", "edge" ], + "eslintConfig": { + "extends": "@adonisjs/eslint-config/package" + }, + "prettier": "@adonisjs/prettier-config", + "commitlint": { + "extends": [ + "@commitlint/config-conventional" + ] + }, "publishConfig": { "access": "public", "tag": "latest" }, "np": { - "contents": ".", + "message": "chore(release): %s", + "tag": "latest", + "branch": "main", "anyBranch": false }, - "mrmConfig": { - "core": false, - "license": "MIT", - "services": [ - "github-actions" - ], - "minNodeVersion": "14.15.4", - "probotApps": [ - "stale", - "lock" + "c8": { + "reporter": [ + "text", + "html" ], - "runGhActionsOnWindows": true + "exclude": [ + "tests/**" + ] }, - "eslintConfig": { - "extends": [ - "plugin:adonis/typescriptPackage", - "prettier" - ], - "plugins": [ - "prettier" + "tsup": { + "entry": [ + "./index.ts", + "./src/types.ts", + "./src/migrate/plugin.ts" ], - "rules": { - "prettier/prettier": [ - "error", - { - "endOfLine": "auto" - } - ] - } - }, - "eslintIgnore": [ - "build" - ], - "prettier": { - "trailingComma": "es5", - "semi": false, - "singleQuote": true, - "useTabs": false, - "quoteProps": "consistent", - "bracketSpacing": true, - "arrowParens": "always", - "printWidth": 100 + "outDir": "./build", + "clean": true, + "format": "esm", + "dts": false, + "sourcemap": true, + "target": "esnext" } } diff --git a/src/Contracts/index.ts b/src/Contracts/index.ts deleted file mode 100644 index 20c7040..0000000 --- a/src/Contracts/index.ts +++ /dev/null @@ -1,406 +0,0 @@ -/** - * edge - * - * (c) Harminder Virk - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -import { Token, TagToken } from 'edge-lexer' -import { MacroableConstructorContract } from 'macroable' -import { ParserTagDefinitionContract, Parser, EdgeBuffer, ClaimTagFn } from 'edge-parser' - -/** - * The shape in which the loader must resolve the template - */ -export type LoaderTemplate = { - template: string -} - -/** - * Loader contract that every loader must adheres to. - */ -export interface LoaderContract { - /** - * List of mounted disks - */ - mounted: { [diskName: string]: string } - - /** - * List of pre-registered template - */ - templates: { [templatePath: string]: LoaderTemplate } - - /** - * Save disk name and dirPath to resolve views - */ - mount(diskName: string, dirPath: string): void - - /** - * Remove disk from the previously saved paths - */ - unmount(diskName: string): void - - /** - * Resolve template contents - */ - resolve(templatePath: string): LoaderTemplate - - /** - * Make absolute path to a template - */ - makePath(templatePath: string): string - - /** - * Register in memory template and presenter - */ - register(templatePath: string, contents: LoaderTemplate): void - - /** - * Remove the pre-registered template - */ - remove(templatePath: string): void -} - -/** - * Shape of template constructor - */ -export interface TemplateConstructorContract - extends MacroableConstructorContract { - new ( - compiler: CompilerContract, - globals: any, - locals: any, - processor: ProcessorContract - ): TemplateContract -} - -/** - * The tag must have a tagName along with other properties - * required by lexer and parser - */ -export interface TagContract extends ParserTagDefinitionContract { - tagName: string - boot?(template: TemplateConstructorContract): void -} - -/** - * Shape of required tags - */ -export type TagsContract = { - [tagName: string]: TagContract -} - -/** - * Shape of the cache manager - */ -export interface CacheManagerContract { - enabled: boolean - get(templatePath: string): undefined | LoaderTemplate - set(templatePath: string, compiledOutput: LoaderTemplate): void - has(templatePath: string): boolean - delete(templatePath: string): void -} - -/** - * Compiler constructor options - */ -export type CompilerOptions = { - cache?: boolean - async?: boolean -} - -/** - * Shape of the compiler - */ -export interface CompilerContract { - cacheManager: CacheManagerContract - async: boolean - claimTag(fn: ClaimTagFn): this - compile(templatePath: string, localVariables?: string[], skipCache?: boolean): LoaderTemplate - tokenize(templatePath: string, parser?: Parser): Token[] - - /** - * Compile the raw string as a template - */ - compileRaw(contents: string, templatePath?: string): LoaderTemplate - - /** - * Tokenize the raw string as a template - */ - tokenizeRaw(contents: string, templatePath?: string, parser?: Parser): Token[] -} - -/** - * Shape of the props class passed to the components - */ -export interface PropsContract { - /** - * Find if a key exists inside the props - */ - has(key: string): boolean - - /** - * Return values for only the given keys - */ - only(keys: string[]): { [key: string]: any } - - /** - * Return values except the given keys - */ - except(keys: string[]): { [key: string]: any } - - /** - * Serialize all props to a string of HTML attributes - */ - serialize(mergeProps?: any): { value: string } - - /** - * Serialize only the given keys to a string of HTML attributes - */ - serializeOnly(keys: string[], mergeProps?: any): { value: string } - - /** - * Serialize except the given keys to a string of HTML attributes - */ - serializeExcept(keys: string[], mergeProps?: any): { value: string } -} - -/** - * Shape of the template contract - */ -export interface TemplateContract { - /** - * Compiles partial - */ - compilePartial(templatePath: string, ...localVariables: string[]): Function - - /** - * Compiles a component - */ - compileComponent(templatePath: string, ...localVariables: string[]): string - - /** - * Returns the state for a component - */ - getComponentState( - props: { [key: string]: any }, - slots: { [key: string]: any }, - caller: { filename: string; line: number; col: number } - ): { - $props: PropsContract & { [key: string]: any } - $slots: { [key: string]: any } - $caller: { filename: string; line: number; col: number } - } - - /** - * Renders a template to a string - */ - render | string>(template: string, state: any): T - renderRaw | string>( - contents: string, - state: any, - templatePath?: string - ): T - - /** - * Escape input - */ - escape(input: any): string - - /** - * Rethrow exceptions by pointing back to edge source file and line number - */ - reThrow(error: any, filename: string, line: number): never -} - -/** - * Shape of the renderer that renders the edge templates - */ -export interface EdgeRendererContract { - /** - * Share state with the template and its partials and component - */ - share(locals: any): this - - /** - * Render a template asynchronously - */ - render(templatePath: string, state?: any): Promise - renderRaw(contents: string, state?: any, templatePath?: string): Promise - - /** - * Render a template synchronously - */ - renderSync(templatePath: string, state?: any): string - renderRawSync(contents: string, state?: any, templatePath?: string): string -} - -/** - * The processor is used to execute process functions for different - * lifecycles - */ -export interface ProcessorContract { - /** - * Hook into the raw text to modify its contents. Make sure to return the - * new string back or return "void" in case no modifications have been - * performed - */ - process(event: 'raw', handler: (data: { raw: string; path: string }) => string | void): this - - /** - * Hook into the tag node to modify its properties - */ - process(event: 'tag', handler: (data: { tag: TagToken; path: string }) => void): this - - /** - * Hook into the compiled template to modify its contents. Make sure to return the - * new string back or return "void" in case no modifications have been - * performed - */ - process( - event: 'compiled', - handler: (data: { compiled: string; path: string }) => string | void - ): this - - /** - * Hook into the compiled output to modify its contents. Make sure to return the - * new string back or return "void" in case no modifications have been - * performed - */ - process( - event: 'output', - handler: (data: { - output: string - template: TemplateContract - state: Record - }) => string | void - ): this -} - -/** - * Shape of options that can be passed to the - * edge constructor - */ -export type EdgeOptions = { - loader?: LoaderContract - cache?: boolean -} - -/** - * Shape of the main module - */ -export interface EdgeContract { - /** - * Loader for loading templates. You can also define a custom loader when creating - * a new instance of edge - */ - loader: LoaderContract - - /** - * Compiler to be used for compiling synchronously - */ - compiler: CompilerContract - - /** - * Compiler to be used for compiling asynchronously - */ - asyncCompiler: CompilerContract - - /** - * Processor reference to hook into the compile and the rendering - * phase of templates - */ - processor: ProcessorContract - - /** - * Set of registered globals. One can define custom globals using `edge.global` - * method - */ - GLOBALS: { [key: string]: any } - - /** - * A custom set of registered tags. One can define a custom tag using `edge.registerTag` - * method - */ - tags: { [name: string]: TagContract } - - /** - * Register a plugin. Plugins are lazily invoked just before the views are rendered. This - * ensures that plugins will receive a fully configured edge instance. - * - * Also plugins are invoked only once. Unless, the `options.recurring` value is set - */ - use( - pluginFn: (edge: this, firstRun: boolean, options: T) => void, - options?: T - ): this - - /** - * Register a custom tag - */ - registerTag(tag: TagContract): this - - /** - * Register an inline template - */ - registerTemplate(templatePath: string, contents: LoaderTemplate): this - - /** - * Remove the template registered using the "registerTemplate" method - */ - removeTemplate(templatePath: string): this - - /** - * Register a global value - */ - global(key: string, value: any): this - - /** - * Mount/disk - */ - mount(diskName: string): this - mount(diskName: string, dirPath: string): this - - /** - * Unmount disk - */ - unmount(diskName: string): this - - /** - * Get access to the underlying template renderer. Each render call - * to edge results in creating an isolated renderer instance. - */ - onRender(callback: (renderer: EdgeRendererContract) => void): this - - /** - * Get a renderer instance to render templates - */ - getRenderer(): EdgeRendererContract - - /** - * Creates a renderer instances and shares the locals with it - */ - share(locals: any): EdgeRendererContract - - /** - * Render a template asynchronously - */ - render(templatePath: string, state?: any): Promise - renderRaw(contents: string, state?: any, templatePath?: string): Promise - - /** - * Render a template synchronously - */ - renderSync(templatePath: string, state?: any): string - renderRawSync(contents: string, state?: any, templatePath?: string): string -} - -/** - * Required for someone creating custom tags - */ -export type EdgeBufferContract = EdgeBuffer -export type ParserContract = Parser -export type TagTokenContract = TagToken -export { ClaimTagFn } diff --git a/src/Edge/index.ts b/src/Edge/index.ts deleted file mode 100644 index 6f6b3a4..0000000 --- a/src/Edge/index.ts +++ /dev/null @@ -1,327 +0,0 @@ -/* - * edge - * - * (c) Harminder Virk - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -import * as Tags from '../Tags' -import { Loader } from '../Loader' -import { Compiler } from '../Compiler' -import { Template } from '../Template' -import { Processor } from '../Processor' -import { EdgeRenderer } from '../Renderer' - -import { - TagContract, - EdgeOptions, - EdgeContract, - LoaderTemplate, - CompilerOptions, - EdgeRendererContract, -} from '../Contracts' - -/** - * Exposes the API to render templates, register custom tags and globals - */ -export class Edge implements EdgeContract { - private executedPlugins: boolean = false - - /** - * Options passed to the compiler instance - */ - private compilerOptions: CompilerOptions = { - cache: !!this.options.cache, - async: false, - } - - /** - * Options passed to the async compiler instance - */ - private asyncCompilerOptions: CompilerOptions = { - cache: !!this.options.cache, - async: true, - } - - /** - * An array of registered plugins - */ - private plugins: { - fn: (edge: Edge, firstRun: boolean, options?: any) => void - options?: any - }[] = [] - - /** - * Array of registered renderer hooks - */ - private renderCallbacks: ((renderer: EdgeRendererContract) => void)[] = [] - - /** - * Reference to the registered processor handlers - */ - public processor = new Processor() - - /** - * Globals are shared with all rendered templates - */ - public GLOBALS: { [key: string]: any } = {} - - /** - * List of registered tags. Adding new tags will only impact - * this list - */ - public tags: { [name: string]: TagContract } = {} - - /** - * The loader to load templates. A loader can read and return - * templates from anywhere. The default loader reads files - * from the disk - */ - public loader = this.options.loader || new Loader() - - /** - * The underlying compiler in use - */ - public compiler = new Compiler(this.loader, this.tags, this.processor, this.compilerOptions) - - /** - * The underlying compiler in use - */ - public asyncCompiler = new Compiler( - this.loader, - this.tags, - this.processor, - this.asyncCompilerOptions - ) - - constructor(private options: EdgeOptions = {}) { - Object.keys(Tags).forEach((name) => this.registerTag(Tags[name])) - } - - /** - * Execute plugins. Since plugins are meant to be called only - * once we empty out the array after first call - */ - private executePlugins() { - if (this.executedPlugins) { - this.plugins.forEach(({ fn, options }) => { - if (options && options.recurring) { - fn(this, false, options) - } - }) - } else { - this.executedPlugins = true - this.plugins.forEach(({ fn, options }) => { - fn(this, true, options) - }) - } - } - - /** - * Register a plugin. Plugin functions are called once just before - * an attempt to render a view is made. - */ - public use( - pluginFn: (edge: this, firstRun: boolean, options: T) => void, - options?: T - ): this { - this.plugins.push({ - fn: pluginFn, - options, - }) - return this - } - - /** - * Mount named directory to use views. Later you can reference - * the views from a named disk as follows. - * - * ``` - * edge.mount('admin', join(__dirname, 'admin')) - * - * edge.render('admin::filename') - * ``` - */ - public mount(diskName: string, dirPath?: string): this { - if (!dirPath) { - dirPath = diskName - diskName = 'default' - } - - this.loader.mount(diskName, dirPath) - return this - } - - /** - * Un Mount a disk from the loader. - * - * ```js - * edge.unmount('admin') - * ``` - */ - public unmount(diskName: string): this { - this.loader.unmount(diskName) - return this - } - - /** - * Add a new global to the edge globals. The globals are available - * to all the templates. - * - * ```js - * edge.global('username', 'virk') - * edge.global('time', () => new Date().getTime()) - * ``` - */ - public global(name: string, value: any): this { - this.GLOBALS[name] = value - return this - } - - /** - * Add a new tag to the tags list. - * - * ```ts - * edge.registerTag('svg', { - * block: false, - * seekable: true, - * - * compile (parser, buffer, token) { - * const fileName = token.properties.jsArg.trim() - * buffer.writeRaw(fs.readFileSync(__dirname, 'assets', `${fileName}.svg`), 'utf-8') - * } - * }) - * ``` - */ - public registerTag(tag: TagContract): this { - if (typeof tag.boot === 'function') { - tag.boot(Template) - } - - this.tags[tag.tagName] = tag - return this - } - - /** - * Register an in-memory template. - * - * ```ts - * edge.registerTemplate('button', { - * template: ``, - * }) - * ``` - * - * Later you can use this template - * - * ```edge - * @component('button', type = 'primary') - * Get started - * @endcomponent - * ``` - */ - public registerTemplate(templatePath: string, contents: LoaderTemplate): this { - this.loader.register(templatePath, contents) - return this - } - - /** - * Remove the template registered using the "registerTemplate" method - */ - public removeTemplate(templatePath: string): this { - this.loader.remove(templatePath) - this.compiler.cacheManager.delete(templatePath) - this.asyncCompiler.cacheManager.delete(templatePath) - return this - } - - /** - * Get access to the underlying template renderer. Each render call - * to edge results in creating an isolated renderer instance. - */ - public onRender(callback: (renderer: EdgeRendererContract) => void): this { - this.renderCallbacks.push(callback) - return this - } - - /** - * Returns a new instance of edge. The instance - * can be used to define locals. - */ - public getRenderer(): EdgeRendererContract { - this.executePlugins() - - const renderer = new EdgeRenderer( - this.compiler, - this.asyncCompiler, - this.GLOBALS, - this.processor - ) - - this.renderCallbacks.forEach((callback) => callback(renderer)) - return renderer - } - - /** - * Render a template with optional state - * - * ```ts - * edge.render('welcome', { greeting: 'Hello world' }) - * ``` - */ - public render(templatePath: string, state?: any): Promise { - return this.getRenderer().render(templatePath, state) - } - - /** - * Render a template asynchronously with optional state - * - * ```ts - * edge.render('welcome', { greeting: 'Hello world' }) - * ``` - */ - public renderSync(templatePath: string, state?: any): string { - return this.getRenderer().renderSync(templatePath, state) - } - - /** - * Render a template with optional state - * - * ```ts - * edge.render('welcome', { greeting: 'Hello world' }) - * ``` - */ - public renderRaw(contents: string, state?: any, templatePath?: string): Promise { - return this.getRenderer().renderRaw(contents, state, templatePath) - } - - /** - * Render a template asynchronously with optional state - * - * ```ts - * edge.render('welcome', { greeting: 'Hello world' }) - * ``` - */ - public renderRawSync(templatePath: string, state?: any): string { - return this.getRenderer().renderRawSync(templatePath, state) - } - - /** - * Share locals with the current view context. - * - * ```js - * const view = edge.getRenderer() - * - * // local state for the current render - * view.share({ foo: 'bar' }) - * - * view.render('welcome') - * ``` - */ - public share(data: any): EdgeRendererContract { - return this.getRenderer().share(data) - } -} diff --git a/src/Loader/index.ts b/src/Loader/index.ts deleted file mode 100644 index 14a5bae..0000000 --- a/src/Loader/index.ts +++ /dev/null @@ -1,268 +0,0 @@ -/** - * edge - * - * (c) Harminder Virk - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -import { readFileSync } from 'fs' -import { join, isAbsolute } from 'path' -import { LoaderContract, LoaderTemplate } from '../Contracts' - -/** - * The job of a loader is to load the template from a given path. - * The base loader (shipped with edge) looks for files on the - * file-system and reads them synchronously. - * - * You are free to define your own loaders that implements the [[LoaderContract]] interface. - */ -export class Loader implements LoaderContract { - /** - * List of mounted directories - */ - private mountedDirs: Map = new Map() - - /** - * List of pre-registered (in-memory) templates - */ - private preRegistered: Map = new Map() - - /** - * Reads the content of a template from the disk. An exception is raised - * when file is missing or if `readFileSync` returns an error. - */ - private readTemplateContents(absPath: string): string { - try { - return readFileSync(absPath, 'utf-8') - } catch (error) { - if (error.code === 'ENOENT') { - throw new Error(`Cannot resolve "${absPath}". Make sure the file exists`) - } else { - throw error - } - } - } - - /** - * Extracts the disk name and the template name from the template - * path expression. - * - * If `diskName` is missing, it will be set to `default`. - * - * ``` - * extractDiskAndTemplateName('users::list') - * // returns ['users', 'list.edge'] - * - * extractDiskAndTemplateName('list') - * // returns ['default', 'list.edge'] - * ``` - */ - private extractDiskAndTemplateName(templatePath: string): [string, string] { - let [disk, ...rest] = templatePath.split('::') - - if (!rest.length) { - rest = [disk] - disk = 'default' - } - - let [template, ext] = rest.join('::').split('.edge') - - /** - * Depreciate dot based path seperators - */ - if (template.indexOf('.') > -1) { - process.emitWarning( - 'DeprecationWarning', - 'edge: dot "." based path seperators are depreciated. We recommend using "/" instead' - ) - template = template.replace(/\./g, '/') - } - - return [disk, `${template}.${ext || 'edge'}`] - } - - /** - * Returns an object of mounted directories with their public - * names. - * - * ```js - * loader.mounted - * // output - * - * { - * default: '/users/virk/code/app/views', - * foo: '/users/virk/code/app/foo', - * } - * ``` - */ - public get mounted(): { [key: string]: string } { - return Array.from(this.mountedDirs).reduce((obj, [key, value]) => { - obj[key] = value - return obj - }, {}) - } - - /** - * Returns an object of templates registered as a raw string - * - * ```js - * loader.templates - * // output - * - * { - * 'form.label': { template: '/users/virk/code/app/form/label' } - * } - * ``` - */ - public get templates(): { [templatePath: string]: LoaderTemplate } { - return Array.from(this.preRegistered).reduce((obj, [key, value]) => { - obj[key] = value - return obj - }, {}) - } - - /** - * Mount a directory with a name for resolving views. If name is set - * to `default`, then you can resolve views without prefixing the - * disk name. - * - * ```js - * loader.mount('default', join(__dirname, 'views')) - * - * // mount a named disk - * loader.mount('admin', join(__dirname, 'admin/views')) - * ``` - */ - public mount(diskName: string, dirPath: string): void { - this.mountedDirs.set(diskName, dirPath) - } - - /** - * Remove the previously mounted dir. - * - * ```js - * loader.unmount('default') - * ``` - */ - public unmount(diskName: string): void { - this.mountedDirs.delete(diskName) - } - - /** - * Make path to a given template. The paths are resolved from the root - * of the mounted directory. - * - * ```js - * loader.makePath('welcome') // returns {diskRootPath}/welcome.edge - * loader.makePath('admin::welcome') // returns {adminRootPath}/welcome.edge - * loader.makePath('users.list') // returns {diskRootPath}/users/list.edge - * ``` - * - * @throws Error if disk is not mounted and attempting to make path for it. - */ - public makePath(templatePath: string): string { - /** - * Return the template path as it is, when it is registered - * dynamically - */ - if (this.preRegistered.has(templatePath)) { - return templatePath - } - - /** - * Return absolute path as it is - */ - if (isAbsolute(templatePath)) { - return templatePath - } - - /** - * Extract disk name and template path from the expression - */ - const [diskName, template] = this.extractDiskAndTemplateName(templatePath) - - /** - * Raise exception when disk name is not registered - */ - const mountedDir = this.mountedDirs.get(diskName) - if (!mountedDir) { - throw new Error(`"${diskName}" namespace is not mounted`) - } - - return join(mountedDir, template) - } - - /** - * Resolves the template by reading its contents from the disk - * - * ```js - * loader.resolve('welcome', true) - * - * // output - * { - * template: `

Template content

`, - * } - * ``` - */ - public resolve(templatePath: string): LoaderTemplate { - /** - * Return from pre-registered one's if exists - */ - if (this.preRegistered.has(templatePath)) { - return this.preRegistered.get(templatePath)! - } - - /** - * Make absolute to the file on the disk - */ - templatePath = isAbsolute(templatePath) ? templatePath : this.makePath(templatePath) - - return { - template: this.readTemplateContents(templatePath), - } - } - - /** - * Register in memory template for a given path. This is super helpful - * when distributing components. - * - * ```js - * loader.register('welcome', { - * template: '

Template content

', - * Presenter: class Presenter { - * constructor (state) { - * this.state = state - * } - * } - * }) - * ``` - * - * @throws Error if template content is empty. - */ - public register(templatePath: string, contents: LoaderTemplate) { - /** - * Ensure template content is defined as a string - */ - if (typeof contents.template !== 'string') { - throw new Error('Make sure to define the template content as a string') - } - - /** - * Do not overwrite existing template with same template path - */ - if (this.preRegistered.has(templatePath)) { - throw new Error(`Cannot override previously registered "${templatePath}" template`) - } - - this.preRegistered.set(templatePath, contents) - } - - /** - * Remove registered template - */ - public remove(templatePath: string) { - this.preRegistered.delete(templatePath) - } -} diff --git a/src/Renderer/index.ts b/src/Renderer/index.ts deleted file mode 100644 index 5f2a6e2..0000000 --- a/src/Renderer/index.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * edge - * - * (c) Harminder Virk - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -import { lodash } from '@poppinss/utils' - -import { Template } from '../Template' -import { Processor } from '../Processor' -import { EdgeRendererContract, CompilerContract } from '../Contracts' - -/** - * Renders a given template with it's shared state - */ -export class EdgeRenderer implements EdgeRendererContract { - private locals: any = {} - - constructor( - private compiler: CompilerContract, - private asyncCompiler: CompilerContract, - private globals: any, - private processor: Processor - ) {} - - /** - * Share local variables with the template. They will overwrite the - * globals - */ - public share(data: any): this { - lodash.merge(this.locals, data) - return this - } - - /** - * Render the template - */ - public async render(templatePath: string, state: any = {}): Promise { - return new Template(this.asyncCompiler, this.globals, this.locals, this.processor).render( - templatePath, - state - ) - } - - /** - * Render the template - */ - public renderSync(templatePath: string, state: any = {}): string { - return new Template(this.compiler, this.globals, this.locals, this.processor).render( - templatePath, - state - ) - } - - /** - * Render the template from a raw string - */ - public async renderRaw( - contents: string, - state: any = {}, - templatePath?: string - ): Promise { - return new Template(this.asyncCompiler, this.globals, this.locals, this.processor).renderRaw( - contents, - state, - templatePath - ) - } - - /** - * Render the template from a raw string - */ - public renderRawSync(contents: string, state: any = {}, templatePath?: string): string { - return new Template(this.compiler, this.globals, this.locals, this.processor).renderRaw( - contents, - state, - templatePath - ) - } -} diff --git a/src/StringifiedObject/index.ts b/src/StringifiedObject/index.ts deleted file mode 100644 index e2bdbde..0000000 --- a/src/StringifiedObject/index.ts +++ /dev/null @@ -1,95 +0,0 @@ -/* - * edge - * - * (c) Harminder Virk - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -import { Parser } from 'edge-parser' - -/** - * This class generates a valid object as a string, which is written to the template - * output. The reason we need a string like object, since we don't want it's - * properties to be evaluated during the object creation, instead it must - * be evaluated when the compiled output is invoked. - */ -export class StringifiedObject { - private obj: string = '' - - public addSpread(key: string) { - this.obj += this.obj.length ? `, ${key}` : `${key}` - } - - /** - * Add key/value pair to the object. - * - * ```js - * stringifiedObject.add('username', `'virk'`) - * ``` - */ - public add(key: any, value: any, isComputed: boolean = false) { - key = isComputed ? `[${key}]` : key - this.obj += this.obj.length ? `, ${key}: ${value}` : `${key}: ${value}` - } - - /** - * Returns the object alike string back. - * - * ```js - * stringifiedObject.flush() - * - * // returns - * `{ username: 'virk' }` - * ``` - */ - public flush(): string { - const obj = `{ ${this.obj} }` - this.obj = '' - return obj - } - - /** - * Parses an array of expressions to form an object. Each expression inside the array must - * be `ObjectExpression` or an `AssignmentExpression`, otherwise it will be ignored. - * - * ```js - * (title = 'hello') - * // returns { title: 'hello' } - * - * ({ title: 'hello' }) - * // returns { title: 'hello' } - * - * ({ title: 'hello' }, username = 'virk') - * // returns { title: 'hello', username: 'virk' } - * ``` - */ - public static fromAcornExpressions(expressions: any[], parser: Parser): string { - if (!Array.isArray(expressions)) { - throw new Error('"fromAcornExpressions" expects an array of acorn ast expressions') - } - - const objectifyString = new this() - - expressions.forEach((arg) => { - if (arg.type === 'ObjectExpression') { - arg.properties.forEach((prop: any) => { - if (prop.type === 'SpreadElement') { - objectifyString.addSpread(parser.utils.stringify(prop)) - } else { - const key = parser.utils.stringify(prop.key) - const value = parser.utils.stringify(prop.value) - objectifyString.add(key, value, prop.computed) - } - }) - } - - if (arg.type === 'AssignmentExpression') { - objectifyString.add(arg.left.name, parser.utils.stringify(arg.right)) - } - }) - - return objectifyString.flush() - } -} diff --git a/src/Tags/index.ts b/src/Tags/index.ts deleted file mode 100644 index abeb701..0000000 --- a/src/Tags/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * edge - * - * (c) Harminder Virk - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -export { ifTag as if } from './If' -export { elseTag as else } from './Else' -export { elseIfTag as elseif } from './ElseIf' -export { includeTag as include } from './Include' -export { includeIfTag as includeIf } from './IncludeIf' -export { eachTag as each } from './Each' -export { componentTag as component } from './Component' -export { slotTag as slot } from './Slot' -export { debuggerTag as debugger } from './Debugger' -export { setTag as set } from './Set' -export { unlessTag as unless } from './Unless' -export { layoutTag as layout } from './Layout' -export { sectionTag as section } from './Section' -export { superTag as super } from './Super' -export { injectTag as inject } from './Inject' -export { newErrorTag as newError } from './NewError' diff --git a/src/Template/index.ts b/src/Template/index.ts deleted file mode 100644 index 705201a..0000000 --- a/src/Template/index.ts +++ /dev/null @@ -1,227 +0,0 @@ -/* - * edge - * - * (c) Harminder Virk - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -import { Macroable } from 'macroable' -import { EdgeError } from 'edge-error' -import { lodash } from '@poppinss/utils' -import { string } from '@poppinss/utils/build/helpers' - -import { Processor } from '../Processor' -import { Props } from '../Component/Props' -import { CompilerContract, TemplateContract } from '../Contracts' - -/** - * An instance of this class passed to the escape - * method ensures that underlying value is never - * escaped. - */ -export class SafeValue { - constructor(public value: any) {} -} - -/** - * Escapes a given string - */ -export function escape(input: any): string { - return input instanceof SafeValue ? input.value : string.escapeHTML(String(input)) -} - -/** - * Mark value as safe and not to be escaped - */ -export function safeValue(value: string) { - return new SafeValue(value) -} - -/** - * The template is used to compile and run templates. Also the instance - * of template is passed during runtime to render `dynamic partials` - * and `dynamic components`. - */ -export class Template extends Macroable implements TemplateContract { - /** - * Required by Macroable - */ - protected static macros = {} - protected static getters = {} - - /** - * The shared state is used to hold the globals and locals, - * since it is shared with components too. - */ - private sharedState: any - - constructor( - private compiler: CompilerContract, - globals: any, - locals: any, - private processor: Processor - ) { - super() - this.sharedState = lodash.merge({}, globals, locals) - } - - /** - * Wraps template to a function - */ - private wrapToFunction(template: string, ...localVariables: string[]) { - const args = ['template', 'state', '$context'].concat(localVariables) - - if (this.compiler.async) { - return new Function( - '', - `return async function template (${args.join(',')}) { ${template} }` - )() - } - - return new Function('', `return function template (${args.join(',')}) { ${template} }`)() - } - - /** - * Trims top and bottom new lines from the content - */ - private trimTopBottomNewLines(value: string) { - return value.replace(/^\n|^\r\n/, '').replace(/\n$|\r\n$/, '') - } - - /** - * Render a compiled template with state - */ - private renderCompiled(compiledTemplate: string, state: any) { - const templateState = Object.assign({}, this.sharedState, state) - const $context = {} - - /** - * Process template as a promise. - */ - if (this.compiler.async) { - return this.wrapToFunction(compiledTemplate)(this, templateState, $context).then( - (output: string) => { - output = this.trimTopBottomNewLines(output) - return this.processor.executeOutput({ output, template: this, state: templateState }) - } - ) - } - - const output = this.trimTopBottomNewLines( - this.wrapToFunction(compiledTemplate)(this, templateState, $context) - ) - - return this.processor.executeOutput({ output, template: this, state: templateState }) - } - - /** - * Render a partial - * - * ```js - * const partialFn = template.compilePartial('includes/user') - * - * // render and use output - * partialFn(template, state, ctx) - * ``` - */ - public compilePartial(templatePath: string, ...localVariables: string[]): Function { - const { template: compiledTemplate } = this.compiler.compile(templatePath, localVariables, true) - return this.wrapToFunction(compiledTemplate, ...localVariables) - } - - /** - * Render a component - * - * ```js - * const componentFn = template.compileComponent('components/button') - * - * // render and use output - * componentFn(template, template.getComponentState(props, slots, caller), ctx) - * ``` - */ - public compileComponent(templatePath: string, ...localVariables: string[]): string { - const { template: compiledTemplate } = this.compiler.compile(templatePath, localVariables) - return this.wrapToFunction(compiledTemplate, ...localVariables) - } - - /** - * Returns the isolated state for a given component - */ - public getComponentState( - props: { [key: string]: any }, - slots: { [key: string]: any }, - caller: { filename: string; line: number; col: number } - ) { - return Object.assign({}, this.sharedState, props, { - $slots: slots, - $caller: caller, - $props: new Props(props), - }) - } - - /** - * Render a template with it's state. - * - * ```js - * template.render('welcome', { key: 'value' }) - * ``` - */ - public render | string>(template: string, state: any): T { - let { template: compiledTemplate } = this.compiler.compile(template) - return this.renderCompiled(compiledTemplate, state) - } - - /** - * Render template from a raw string - * - * ```js - * template.renderRaw('Hello {{ username }}', { username: 'virk' }) - * ``` - */ - public renderRaw | string>( - contents: string, - state: any, - templatePath?: string - ): T { - let { template: compiledTemplate } = this.compiler.compileRaw(contents, templatePath) - return this.renderCompiled(compiledTemplate, state) - } - - /** - * Escapes the value to be HTML safe. Only strings are escaped - * and rest all values will be returned as it is. - */ - public escape(input: any): string { - return escape(input) - } - - /** - * Raise an error - */ - public newError(errorMessage: string, filename: string, lineNumber: number, column: number) { - throw new EdgeError(errorMessage, 'E_RUNTIME_EXCEPTION', { - filename: filename, - line: lineNumber, - col: column, - }) - } - - /** - * Rethrows the runtime exception by re-constructing the error message - * to point back to the original filename - */ - public reThrow(error: any, filename: string, lineNumber: number): never { - if (error instanceof EdgeError) { - throw error - } - - const message = error.message.replace(/state\./, '') - throw new EdgeError(message, 'E_RUNTIME_EXCEPTION', { - filename: filename, - line: lineNumber, - col: 0, - }) - } -} diff --git a/src/CacheManager/index.ts b/src/cache_manager.ts similarity index 62% rename from src/CacheManager/index.ts rename to src/cache_manager.ts index a61d354..879cef9 100644 --- a/src/CacheManager/index.ts +++ b/src/cache_manager.ts @@ -1,19 +1,19 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { LoaderTemplate, CacheManagerContract } from '../Contracts' +import type { CacheManagerContract, CompiledTemplate } from './types.js' /** * In memory cache manager to cache pre-compiled templates. */ export class CacheManager implements CacheManagerContract { - private cacheStore: Map = new Map() + #cacheStore: Map = new Map() constructor(public enabled: boolean) {} @@ -21,42 +21,42 @@ export class CacheManager implements CacheManagerContract { * Returns a boolean to tell if a template has already been cached * or not. */ - public has(absPath: string): boolean { - return this.cacheStore.has(absPath) + has(absPath: string): boolean { + return this.#cacheStore.has(absPath) } /** * Returns the template from the cache. If caching is disabled, * then it will return undefined. */ - public get(absPath: string): undefined | LoaderTemplate { + get(absPath: string): undefined | CompiledTemplate { if (!this.enabled) { return } - return this.cacheStore.get(absPath) + return this.#cacheStore.get(absPath) } /** * Set's the template path and the payload to the cache. If * cache is disabled, then this function results in a noop. */ - public set(absPath: string, payload: LoaderTemplate) { + set(absPath: string, payload: CompiledTemplate) { if (!this.enabled) { return } - this.cacheStore.set(absPath, payload) + this.#cacheStore.set(absPath, payload) } /** * Delete template from the compiled cache */ - public delete(absPath: string) { + delete(absPath: string) { if (!this.enabled) { return } - this.cacheStore.delete(absPath) + this.#cacheStore.delete(absPath) } } diff --git a/src/Compiler/index.ts b/src/compiler.ts similarity index 56% rename from src/Compiler/index.ts rename to src/compiler.ts index d7c7519..60ad433 100644 --- a/src/Compiler/index.ts +++ b/src/compiler.ts @@ -1,58 +1,88 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ import { EdgeError } from 'edge-error' +import * as lexerUtils from 'edge-lexer/utils' import { Parser, EdgeBuffer, Stack } from 'edge-parser' -import { Token, TagToken, utils as lexerUtils } from 'edge-lexer' +import type { Token, TagToken } from 'edge-lexer/types' -import { Processor } from '../Processor' -import { CacheManager } from '../CacheManager' -import { +import { Processor } from './processor.js' +import { CacheManager } from './cache_manager.js' +import type { ClaimTagFn, TagsContract, LoaderContract, - LoaderTemplate, CompilerOptions, - CompilerContract, -} from '../Contracts' + CompiledTemplate, +} from './types.js' + +const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor /** * Compiler is to used to compile templates using the `edge-parser`. Along with that * it natively merges the contents of a layout with a parent template. */ -export class Compiler implements CompilerContract { - private claimTagFn?: ClaimTagFn +export class Compiler { + /** + * The variables someone can access inside templates. All other + * variables will get prefixed with `state` property name + */ + #inlineVariables: string[] = ['$filename', 'state', '$context'] + + /** + * A fixed set of params to pass to the template every time. + */ + #templateParams = ['template', 'state', '$context'] + + #claimTagFn?: ClaimTagFn + #loader: LoaderContract + #tags: TagsContract + #processor: Processor /** * Caches compiled templates */ - public cacheManager = new CacheManager(!!this.options.cache) + cacheManager: CacheManager /** - * Know if compiler is compiling for the async mode or not + * A boolean to know if compat mode is enabled */ - public async = !!this.options.async + compat: boolean + + /** + * Know if compiler is compiling in the async mode or not + */ + async: boolean constructor( - private loader: LoaderContract, - private tags: TagsContract, - private processor: Processor, - private options: CompilerOptions = { + loader: LoaderContract, + tags: TagsContract, + processor: Processor, + options: CompilerOptions = { cache: true, async: false, + compat: false, } - ) {} + ) { + this.#processor = processor + this.#loader = loader + this.#tags = tags + + this.async = !!options.async + this.compat = options.compat === true + this.cacheManager = new CacheManager(!!options.cache) + } /** * Merges sections of base template and parent template tokens */ - private mergeSections(base: Token[], extended: Token[]): Token[] { + #mergeSections(base: Token[], extended: Token[]): Token[] { /** * Collection of all sections from the extended tokens */ @@ -148,17 +178,23 @@ export class Compiler implements CompilerContract { * are checked for layouts and if layouts are used, their sections will be * merged together. */ - private templateContentToTokens(content: string, parser: Parser, absPath: string): Token[] { + #templateContentToTokens(content: string, parser: Parser, absPath: string): Token[] { let templateTokens = parser.tokenize(content, { filename: absPath }) - const firstToken = templateTokens[0] /** - * The `layout` is inbuilt feature from core, where we merge the layout - * and parent template sections together + * Parse layout and section in compat mode only */ - if (lexerUtils.isTag(firstToken, 'layout')) { - const layoutName = firstToken.properties.jsArg.replace(/'|"/g, '') - templateTokens = this.mergeSections(this.tokenize(layoutName, parser), templateTokens) + if (this.compat) { + const firstToken = templateTokens[0] + + /** + * The `layout` is inbuilt feature from core, where we merge the layout + * and parent template sections together + */ + if (lexerUtils.isTag(firstToken, 'layout')) { + const layoutName = firstToken.properties.jsArg.replace(/'|"/g, '') + templateTokens = this.#mergeSections(this.tokenize(layoutName, parser), templateTokens) + } } return templateTokens @@ -167,14 +203,14 @@ export class Compiler implements CompilerContract { /** * Returns the parser instance for a given template */ - private getParserFor(templatePath: string, localVariables?: string[]) { - const parser = new Parser(this.tags, new Stack(), { - claimTag: this.claimTagFn, + #getParserFor(templatePath: string, localVariables?: string[]) { + const parser = new Parser(this.#tags, new Stack(), { + claimTag: this.#claimTagFn, async: this.async, statePropertyName: 'state', escapeCallPath: ['template', 'escape'], - localVariables: ['$filename', 'state', '$context'], - onTag: (tag) => this.processor.executeTag({ tag, path: templatePath }), + localVariables: this.#inlineVariables, + onTag: (tag) => this.#processor.executeTag({ tag, path: templatePath }), }) /** @@ -192,18 +228,31 @@ export class Compiler implements CompilerContract { /** * Returns the parser instance for a given template */ - private getBufferFor(templatePath: string) { + #getBufferFor(templatePath: string) { return new EdgeBuffer(templatePath, { outputVar: 'out', rethrowCallPath: ['template', 'reThrow'], }) } + /** + * Wraps template output to a function along with local variables + */ + #wrapToFunction(template: string, localVariables?: string[]): CompiledTemplate { + const args = localVariables ? this.#templateParams.concat(localVariables) : this.#templateParams + + if (this.async) { + return new AsyncFunction(...args, template) + } + + return new Function(...args, template) as CompiledTemplate + } + /** * Define a function to claim tags */ - public claimTag(fn: ClaimTagFn): this { - this.claimTagFn = fn + claimTag(fn: ClaimTagFn): this { + this.#claimTagFn = fn return this } @@ -215,24 +264,20 @@ export class Compiler implements CompilerContract { * compiler.tokenize('') * ``` */ - public tokenize(templatePath: string, parser?: Parser): Token[] { - const absPath = this.loader.makePath(templatePath) - let { template } = this.loader.resolve(absPath) + tokenize(templatePath: string, parser?: Parser): Token[] { + const absPath = this.#loader.makePath(templatePath) + let { template } = this.#loader.resolve(absPath) return this.tokenizeRaw(template, absPath, parser) } /** * Tokenize a raw template */ - public tokenizeRaw( - contents: string, - templatePath: string = 'eval.edge', - parser?: Parser - ): Token[] { - contents = this.processor.executeRaw({ path: templatePath, raw: contents }) - return this.templateContentToTokens( + tokenizeRaw(contents: string, templatePath: string = 'eval.edge', parser?: Parser): Token[] { + contents = this.#processor.executeRaw({ path: templatePath, raw: contents }) + return this.#templateContentToTokens( contents, - parser || this.getParserFor(templatePath), + parser || this.#getParserFor(templatePath), templatePath ) } @@ -246,20 +291,16 @@ export class Compiler implements CompilerContract { * compiler.compile('welcome') * ``` */ - public compile( - templatePath: string, - localVariables?: string[], - skipCache = false - ): LoaderTemplate { - const absPath = this.loader.makePath(templatePath) - let cachedResponse = skipCache ? null : this.cacheManager.get(absPath) + compile(templatePath: string, localVariables?: string[]): CompiledTemplate { + const absPath = this.#loader.makePath(templatePath) + let cachedResponse = localVariables ? null : this.cacheManager.get(absPath) /** * Process the template and cache it */ if (!cachedResponse) { - const parser = this.getParserFor(absPath, localVariables) - const buffer = this.getBufferFor(absPath) + const parser = this.#getParserFor(absPath, localVariables) + const buffer = this.#getBufferFor(absPath) /** * Generate tokens and process them @@ -267,20 +308,23 @@ export class Compiler implements CompilerContract { const templateTokens = this.tokenize(absPath, parser) templateTokens.forEach((token) => parser.processToken(token, buffer)) - const template = buffer.flush() - if (!skipCache) { - this.cacheManager.set(absPath, { template }) + /** + * Processing template via hook + */ + const template = this.#processor.executeCompiled({ + path: absPath, + compiled: buffer.flush(), + }) + + const compiledTemplate = this.#wrapToFunction(template, localVariables) + if (!localVariables) { + this.cacheManager.set(absPath, compiledTemplate) } - cachedResponse = { template } + cachedResponse = compiledTemplate } - const template = this.processor.executeCompiled({ - path: absPath, - compiled: cachedResponse.template, - }) - - return { template } + return cachedResponse! } /** @@ -289,21 +333,21 @@ export class Compiler implements CompilerContract { * handles layouts. * * ```js - * compiler.compile('welcome') + * compiler.compileRaw('welcome') * ``` */ - public compileRaw(contents: string, templatePath: string = 'eval.edge'): LoaderTemplate { - const parser = this.getParserFor(templatePath) - const buffer = this.getBufferFor(templatePath) + compileRaw(contents: string, templatePath: string = 'eval.edge'): CompiledTemplate { + const parser = this.#getParserFor(templatePath) + const buffer = this.#getBufferFor(templatePath) const templateTokens = this.tokenizeRaw(contents, templatePath, parser) templateTokens.forEach((token) => parser.processToken(token, buffer)) - const template = this.processor.executeCompiled({ + const template = this.#processor.executeCompiled({ path: templatePath, compiled: buffer.flush(), }) - return { template } + return this.#wrapToFunction(template) } } diff --git a/src/component/props.ts b/src/component/props.ts new file mode 100644 index 0000000..6dcb4d7 --- /dev/null +++ b/src/component/props.ts @@ -0,0 +1,119 @@ +/* + * edge.js + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import lodash from '@poppinss/utils/lodash' +import { htmlSafe } from '../template.js' +import { stringifyAttributes } from '../utils.js' + +/** + * Representation of component props with ability to serialize + * them into HTML attributes + */ +export class ComponentProps { + #values: Record + + constructor(values: Record) { + this.#values = values + Object.assign(this, values) + } + + /** + * Create a typed instance of Component props with properties + */ + static create>(values: T): ComponentProps & T { + return new ComponentProps(values) as ComponentProps & T + } + + /** + * Reference to props raw values + */ + all() { + return this.#values + } + + /** + * Check if a key exists + */ + has(key: string) { + return lodash.has(this.#values, key) + } + + /** + * Get key value + */ + get(key: string, defaultValue?: any) { + return lodash.get(this.#values, key, defaultValue) + } + + /** + * Returns a new props bag with only the mentioned keys + */ + only(keys: string[]) { + return new ComponentProps(lodash.pick(this.#values, keys)) + } + + /** + * Returns a new props bag with except the mentioned keys + */ + except(keys: string[]) { + return new ComponentProps(lodash.omit(this.#values, keys)) + } + + /** + * Merge defaults with the props + * + * - All other attributes will be overwritten when defined in props + * - Classes will be merged together. + */ + merge(values: Record) { + if (values.class && this.#values['class']) { + const classesSet: Set = new Set() + ;(Array.isArray(values.class) ? values.class : [values]).forEach((item) => { + classesSet.add(item) + }) + ;(Array.isArray(this.#values['class']) + ? this.#values['class'] + : [this.#values['class']] + ).forEach((item) => { + classesSet.add(item) + }) + + return new ComponentProps({ ...values, ...this.#values, class: Array.from(classesSet) }) + } + + return new ComponentProps({ ...values, ...this.#values }) + } + + /** + * Merge defaults with the props, if the given condition is truthy + */ + mergeIf(conditional: any, values: Record) { + if (conditional) { + return this.merge(values) + } + return this + } + + /** + * Merge defaults with the props, if the given condition is falsy + */ + mergeUnless(conditional: any, values: Record) { + if (!conditional) { + return this.merge(values) + } + return this + } + + /** + * Converts props to HTML attributes + */ + toAttrs() { + return htmlSafe(stringifyAttributes(this.#values)) + } +} diff --git a/src/edge/globals.ts b/src/edge/globals.ts new file mode 100644 index 0000000..00b8e4c --- /dev/null +++ b/src/edge/globals.ts @@ -0,0 +1,108 @@ +/* + * edge.js.js + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +// @ts-expect-error untyped module +import stringify from 'js-stringify' +import classNames from 'classnames' +// @ts-expect-error untyped module +import inspect from '@poppinss/inspect' +import string from '@poppinss/utils/string' + +import type { EdgeGlobals } from '../types.js' +import { htmlSafe, escape } from '../template.js' +import { stringifyAttributes } from '../utils.js' + +/** + * Inbuilt globals + */ +export const edgeGlobals: EdgeGlobals = { + /** + * Converts new lines to break + */ + nl2br: (value: string | null | undefined) => { + if (!value) { + return + } + + return String(value).replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1
') + }, + + /** + * Inspect state + */ + inspect: (value: any) => { + return htmlSafe(inspect.string.html(value)) + }, + + /** + * Truncate a sentence + */ + truncate: ( + value: string, + length: number = 20, + options?: { completeWords?: boolean; strict?: boolean; suffix?: string } + ) => { + options = options || {} + return string.truncate(value, length, { + completeWords: options.completeWords !== undefined ? options.completeWords : !options.strict, + suffix: options.suffix, + }) + }, + + /** + * Generate an excerpt + */ + excerpt: ( + value: string, + length: number = 20, + options?: { completeWords?: boolean; strict?: boolean; suffix?: string } + ) => { + options = options || {} + return string.excerpt(value, length, { + completeWords: options.completeWords !== undefined ? options.completeWords : !options.strict, + suffix: options.suffix, + }) + }, + + /** + * Helpers related to HTML + */ + html: { + escape: escape, + safe: htmlSafe, + classNames: classNames, + attrs: (values: Record) => { + return htmlSafe(stringifyAttributes(values)) + }, + }, + + /** + * Helpers related to JavaScript + */ + js: { + stringify: stringify, + }, + + camelCase: string.camelCase, + snakeCase: string.snakeCase, + dashCase: string.dashCase, + pascalCase: string.pascalCase, + capitalCase: string.capitalCase, + sentenceCase: string.sentenceCase, + dotCase: string.dotCase, + noCase: string.noCase, + titleCase: string.titleCase, + pluralize: string.pluralize, + sentence: string.sentence, + prettyMs: string.milliseconds.format, + toMs: string.milliseconds.parse, + prettyBytes: string.bytes.format, + toBytes: string.bytes.parse, + ordinal: string.ordinal, +} diff --git a/src/edge/main.ts b/src/edge/main.ts new file mode 100644 index 0000000..facd557 --- /dev/null +++ b/src/edge/main.ts @@ -0,0 +1,381 @@ +/* + * edge.js + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { Loader } from '../loader.js' +import * as Tags from '../tags/main.js' +import { Compiler } from '../compiler.js' +import { Template } from '../template.js' +import { edgeGlobals } from './globals.js' +import { Processor } from '../processor.js' +import { EdgeRenderer } from './renderer.js' +import type { + PluginFn, + TagContract, + EdgeGlobals, + EdgeOptions, + LoaderTemplate, + LoaderContract, +} from '../types.js' +import { pluginSuperCharged } from '../plugins/supercharged.js' + +/** + * Exposes the API to render templates, register custom tags and globals + */ +export class Edge { + /** + * Create an instance of edge with given options + */ + static create(options: EdgeOptions = {}) { + return new Edge(options) + } + + /** + * An array of bundled plugins + */ + #bundledPlugins: { + fn: PluginFn + executed: boolean + options?: any + }[] = [] + + /** + * An array of registered plugins + */ + #plugins: { + fn: PluginFn + executed: boolean + options?: any + }[] = [] + + /** + * Array of registered renderer hooks + */ + #renderCallbacks: ((renderer: EdgeRenderer) => void)[] = [] + + /** + * Reference to the registered processor handlers + */ + processor = new Processor() + + /** + * A flag to know if using compat mode + */ + compat: boolean = false + + /** + * The loader to load templates. A loader can read and return + * templates from anywhere. The default loader reads files + * from the disk + */ + declare loader: LoaderContract + + /** + * The underlying compiler in use + */ + declare compiler: Compiler + + /** + * The underlying compiler in use + */ + declare asyncCompiler: Compiler + + /** + * Globals are shared with all rendered templates + */ + globals: EdgeGlobals = { ...edgeGlobals } + + /** + * List of registered tags. Adding new tags will only impact + * this list + */ + tags: { [name: string]: TagContract } = {} + + constructor(options: EdgeOptions = {}) { + this.configure(options) + + /** + * Registering bundled set of tags + */ + Object.keys(Tags).forEach((name) => { + this.registerTag(Tags[name as keyof typeof Tags]) + }) + + this.#bundledPlugins.push({ + fn: pluginSuperCharged, + executed: false, + options: { recurring: !options.cache }, + }) + } + + /** + * Re-configure an existing edge instance + */ + configure(options: EdgeOptions) { + if (options.loader) { + this.loader = options.loader + } else if (!this.loader) { + this.loader = new Loader() + } + + this.compiler = new Compiler(this.loader, this.tags, this.processor, { + cache: !!options.cache, + async: false, + }) + + this.asyncCompiler = new Compiler(this.loader, this.tags, this.processor, { + cache: !!options.cache, + async: true, + }) + } + + /** + * Execute plugins + */ + #executePlugins() { + /** + * Running user-land plugins + */ + this.#plugins + .filter(({ options, executed }) => { + if (options && options.recurring) { + return true + } + return !executed + }) + .forEach((plugin) => { + plugin.fn(this, !plugin.executed, plugin.options) + plugin.executed = true + }) + + /** + * Running bundled plugins after the user-land + * plugins + */ + this.#bundledPlugins + .filter(({ options, executed }) => { + if (options && options.recurring) { + return true + } + return !executed + }) + .forEach((plugin) => { + plugin.fn(this, !plugin.executed, plugin.options) + plugin.executed = true + }) + } + + /** + * Register a plugin. Plugins are called only once just before + * a rendering a view. + * + * You can invoke a plugin multiple times by marking it as a + * recurring plugin + */ + use(pluginFn: PluginFn, options?: T): this { + this.#plugins.push({ + fn: pluginFn, + executed: false, + options, + }) + return this + } + + /** + * Mount named directory to use views. Later you can reference + * the views from a named disk as follows. + * + * ``` + * edge.mount('admin', join(__dirname, 'admin')) + * + * edge.render('admin::filename') + * ``` + */ + mount(viewsDirectory: string | URL): this + mount(diskName: string, viewsDirectory: string | URL): this + mount(diskName: string | URL, viewsDirectory?: string | URL): this { + if (!viewsDirectory) { + viewsDirectory = diskName + diskName = 'default' + } + + this.loader.mount(diskName as string, viewsDirectory) + return this + } + + /** + * Un Mount a disk from the loader. + * + * ```js + * edge.unmount('admin') + * ``` + */ + unmount(diskName: string): this { + this.loader.unmount(diskName) + return this + } + + /** + * Add a new global to the edge globals. The globals are available + * to all the templates. + * + * ```js + * edge.global('username', 'virk') + * edge.global('time', () => new Date().getTime()) + * ``` + */ + global(name: string, value: any): this { + this.globals[name] = value + return this + } + + /** + * Add a new tag to the tags list. + * + * ```ts + * edge.registerTag('svg', { + * block: false, + * seekable: true, + * + * compile (parser, buffer, token) { + * const fileName = token.properties.jsArg.trim() + * buffer.writeRaw(fs.readFileSync(__dirname, 'assets', `${fileName}.svg`), 'utf-8') + * } + * }) + * ``` + */ + registerTag(tag: TagContract): this { + if (typeof tag.boot === 'function') { + tag.boot(Template) + } + + this.tags[tag.tagName] = tag + return this + } + + /** + * Register an in-memory template. + * + * ```ts + * edge.registerTemplate('button', { + * template: ``, + * }) + * ``` + * + * Later you can use this template + * + * ```edge + * @component('button', type = 'primary') + * Get started + * @endcomponent + * ``` + */ + registerTemplate(templatePath: string, contents: LoaderTemplate): this { + this.loader.register(templatePath, contents) + return this + } + + /** + * Remove the template registered using the "registerTemplate" method + */ + removeTemplate(templatePath: string): this { + this.loader.remove(templatePath) + this.compiler.cacheManager.delete(templatePath) + this.asyncCompiler.cacheManager.delete(templatePath) + return this + } + + /** + * Get access to the underlying template renderer. Each render call + * to edge results in creating an isolated renderer instance. + */ + onRender(callback: (renderer: EdgeRenderer) => void): this { + this.#renderCallbacks.push(callback) + return this + } + + /** + * Returns a new instance of edge. The instance + * can be used to define locals. + */ + createRenderer(): EdgeRenderer { + this.#executePlugins() + + const renderer = new EdgeRenderer( + this.compiler, + this.asyncCompiler, + this.processor, + this.globals + ) + + this.#renderCallbacks.forEach((callback) => callback(renderer)) + return renderer + } + + /** + * Render a template with optional state + * + * ```ts + * edge.render('welcome', { greeting: 'Hello world' }) + * ``` + */ + render(templatePath: string, state?: Record): Promise { + return this.createRenderer().render(templatePath, state) + } + + /** + * Render a template asynchronously with optional state + * + * ```ts + * edge.render('welcome', { greeting: 'Hello world' }) + * ``` + */ + renderSync(templatePath: string, state?: Record): string { + return this.createRenderer().renderSync(templatePath, state) + } + + /** + * Render a template with optional state + * + * ```ts + * edge.render('welcome', { greeting: 'Hello world' }) + * ``` + */ + renderRaw(contents: string, state?: Record, templatePath?: string): Promise { + return this.createRenderer().renderRaw(contents, state, templatePath) + } + + /** + * Render a template asynchronously with optional state + * + * ```ts + * edge.render('welcome', { greeting: 'Hello world' }) + * ``` + */ + renderRawSync(templatePath: string, state?: Record): string { + return this.createRenderer().renderRawSync(templatePath, state) + } + + /** + * Share locals with the current view context. + * + * ```js + * const view = edge.createRenderer() + * + * // local state for the current render + * view.share({ foo: 'bar' }) + * + * view.render('welcome') + * ``` + */ + share(data: Record): EdgeRenderer { + return this.createRenderer().share(data) + } +} diff --git a/src/edge/renderer.ts b/src/edge/renderer.ts new file mode 100644 index 0000000..9f425cd --- /dev/null +++ b/src/edge/renderer.ts @@ -0,0 +1,100 @@ +/* + * edge.js + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import lodash from '@poppinss/utils/lodash' + +import { Template } from '../template.js' +import { Processor } from '../processor.js' +import type { Compiler } from '../compiler.js' + +/** + * Renders a given template with it's shared state + */ +export class EdgeRenderer { + #compiler: Compiler + #processor: Processor + #asyncCompiler: Compiler + + /** + * Global state + */ + #locals: Record = {} + #globals: Record + + constructor( + compiler: Compiler, + asyncCompiler: Compiler, + processor: Processor, + globals: Record + ) { + this.#compiler = compiler + this.#asyncCompiler = asyncCompiler + this.#processor = processor + + this.#globals = globals + } + + /** + * Share local variables with the template. They will overwrite the + * globals + */ + share(data: Record): this { + lodash.merge(this.#locals, data) + return this + } + + /** + * Render the template + */ + async render(templatePath: string, state: Record = {}): Promise { + return new Template(this.#asyncCompiler, this.#globals, this.#locals, this.#processor).render( + templatePath, + state + ) + } + + /** + * Render the template + */ + renderSync(templatePath: string, state: Record = {}): string { + return new Template( + this.#compiler, + this.#globals, + this.#locals, + this.#processor + ).render(templatePath, state) + } + + /** + * Render the template from a raw string + */ + async renderRaw( + contents: string, + state: Record = {}, + templatePath?: string + ): Promise { + return new Template( + this.#asyncCompiler, + this.#globals, + this.#locals, + this.#processor + ).renderRaw(contents, state, templatePath) + } + + /** + * Render the template from a raw string + */ + renderRawSync(contents: string, state: Record = {}, templatePath?: string): string { + return new Template(this.#compiler, this.#globals, this.#locals, this.#processor).renderRaw( + contents, + state, + templatePath + ) + } +} diff --git a/src/loader.ts b/src/loader.ts new file mode 100644 index 0000000..b028cbd --- /dev/null +++ b/src/loader.ts @@ -0,0 +1,365 @@ +/** + * edge + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { slash } from '@poppinss/utils' +import { fileURLToPath } from 'node:url' +import string from '@poppinss/utils/string' +import { join, isAbsolute } from 'node:path' +import readdirSync from 'fs-readdir-recursive' +import { existsSync, readFileSync } from 'node:fs' +import type { ComponentsTree, LoaderContract, LoaderTemplate } from './types.js' + +/** + * The job of a loader is to load the template from a given path. + * The base loader (shipped with edge) looks for files on the + * file-system and reads them synchronously. + * + * You are free to define your own loaders that implements the [[LoaderContract]] interface. + */ +export class Loader implements LoaderContract { + /** + * List of mounted directories + */ + #mountedDirs: Map = new Map() + + /** + * List of pre-registered (in-memory) templates + */ + #preRegistered: Map = new Map() + + /** + * Reads the content of a template from the disk. An exception is raised + * when file is missing or if `readFileSync` returns an error. + */ + #readTemplateContents(absPath: string): string { + try { + return readFileSync(absPath, 'utf-8') + } catch (error) { + if (error.code === 'ENOENT') { + throw new Error(`Cannot resolve "${absPath}". Make sure the file exists`) + } else { + throw error + } + } + } + + /** + * Returns a list of components for a given disk + */ + #getDiskComponents(diskName: string): ComponentsTree[0]['components'] { + const componentsDirName = 'components' + const diskBasePath = this.#mountedDirs.get(diskName)! + let files = + diskName === 'default' + ? Array.from(this.#preRegistered.keys()).map((template) => { + return { + fileName: template, + componentPath: template, + } + }) + : [] + + /** + * Read disk files + */ + if (existsSync(join(diskBasePath, componentsDirName))) { + files = files.concat( + readdirSync(join(diskBasePath, componentsDirName)) + .filter((file) => file.endsWith('.edge')) + .map((template) => { + const fileName = slash(template).replace(/\.edge$/, '') + return { + fileName, + componentPath: `${componentsDirName}/${fileName}`, + } + }) + ) + } + + return files.map(({ fileName, componentPath }) => { + const tagName = fileName + .split('/') + .filter((segment, index) => { + return index === 0 || segment !== 'index' + }) + .map((segment) => string.camelCase(segment)) + .join('.') + + return { + componentName: diskName !== 'default' ? `${diskName}::${componentPath}` : componentPath, + tagName: diskName !== 'default' ? `${diskName}.${tagName}` : tagName, + } + }) + } + + /** + * Returns a list of templates for a given disk + */ + #getDiskTemplates(diskName: string): string[] { + const diskBasePath = this.#mountedDirs.get(diskName)! + let files = diskName === 'default' ? Array.from(this.#preRegistered.keys()) : [] + + if (existsSync(diskBasePath)) { + files = files.concat(readdirSync(join(diskBasePath)).filter((file) => file.endsWith('.edge'))) + } + + return files.map((file) => { + const fileName = slash(file).replace(/\.edge$/, '') + return diskName !== 'default' ? `${diskName}::${fileName}` : fileName + }) + } + + /** + * Extracts the disk name and the template name from the template + * path expression. + * + * If `diskName` is missing, it will be set to `default`. + * + * ``` + * extractDiskAndTemplateName('users::list') + * // returns ['users', 'list.edge'] + * + * extractDiskAndTemplateName('list') + * // returns ['default', 'list.edge'] + * ``` + */ + #extractDiskAndTemplateName(templatePath: string): [string, string] { + let [disk, ...rest] = templatePath.split('::') + + if (!rest.length) { + rest = [disk] + disk = 'default' + } + + let [template, ext] = rest.join('::').split('.edge') + return [disk, `${template}.${ext || 'edge'}`] + } + + /** + * Returns an object of mounted directories with their public + * names. + * + * ```js + * loader.mounted + * // output + * + * { + * default: '/users/virk/code/app/views', + * foo: '/users/virk/code/app/foo', + * } + * ``` + */ + get mounted(): { [key: string]: string } { + return Array.from(this.#mountedDirs).reduce( + (obj, [key, value]) => { + obj[key] = value + return obj + }, + {} as Record + ) + } + + /** + * Returns an object of templates registered as a raw string + * + * ```js + * loader.templates + * // output + * + * { + * 'form.label': { template: 'Template contents' } + * } + * ``` + */ + get templates(): { [templatePath: string]: LoaderTemplate } { + return Array.from(this.#preRegistered).reduce( + (obj, [key, value]) => { + obj[key] = value + return obj + }, + {} as Record + ) + } + + /** + * Mount a directory with a name for resolving views. If name is set + * to `default`, then you can resolve views without prefixing the + * disk name. + * + * ```js + * loader.mount('default', join(__dirname, 'views')) + * + * // mount a named disk + * loader.mount('admin', join(__dirname, 'admin/views')) + * ``` + */ + mount(diskName: string, dirPath: string | URL): void { + this.#mountedDirs.set(diskName, typeof dirPath === 'string' ? dirPath : fileURLToPath(dirPath)) + } + + /** + * Remove the previously mounted dir. + * + * ```js + * loader.unmount('default') + * ``` + */ + unmount(diskName: string): void { + this.#mountedDirs.delete(diskName) + } + + /** + * Make path to a given template. The paths are resolved from the root + * of the mounted directory. + * + * ```js + * loader.makePath('welcome') // returns {diskRootPath}/welcome.edge + * loader.makePath('admin::welcome') // returns {adminRootPath}/welcome.edge + * loader.makePath('users.list') // returns {diskRootPath}/users/list.edge + * ``` + * + * @throws Error if disk is not mounted and attempting to make path for it. + */ + makePath(templatePath: string): string { + /** + * Return the template path as it is, when it is registered + * dynamically + */ + if (this.#preRegistered.has(templatePath)) { + return templatePath + } + + /** + * Return absolute path as it is + */ + if (isAbsolute(templatePath)) { + return templatePath + } + + /** + * Extract disk name and template path from the expression + */ + const [diskName, template] = this.#extractDiskAndTemplateName(templatePath) + + /** + * Raise exception when disk name is not registered + */ + const mountedDir = this.#mountedDirs.get(diskName) + if (!mountedDir) { + throw new Error(`"${diskName}" namespace is not mounted`) + } + + return join(mountedDir, template) + } + + /** + * Resolves the template by reading its contents from the disk + * + * ```js + * loader.resolve('welcome', true) + * + * // output + * { + * template: `

Template content

`, + * } + * ``` + */ + resolve(templatePath: string): LoaderTemplate { + /** + * Return from pre-registered one's if exists + */ + if (this.#preRegistered.has(templatePath)) { + return this.#preRegistered.get(templatePath)! + } + + /** + * Make absolute to the file on the disk + */ + templatePath = isAbsolute(templatePath) ? templatePath : this.makePath(templatePath) + + return { + template: this.#readTemplateContents(templatePath), + } + } + + /** + * Register in memory template for a given path. This is super helpful + * when distributing components. + * + * ```js + * loader.register('welcome', { + * template: '

Template content

', + * Presenter: class Presenter { + * constructor (state) { + * this.state = state + * } + * } + * }) + * ``` + * + * @throws Error if template content is empty. + */ + register(templatePath: string, contents: LoaderTemplate) { + /** + * Ensure template content is defined as a string + */ + if (typeof contents.template !== 'string') { + throw new Error('Make sure to define the template content as a string') + } + + /** + * Do not overwrite existing template with same template path + */ + if (this.#preRegistered.has(templatePath)) { + throw new Error(`Cannot override previously registered "${templatePath}" template`) + } + + this.#preRegistered.set(templatePath, contents) + } + + /** + * Remove registered template + */ + remove(templatePath: string) { + this.#preRegistered.delete(templatePath) + } + + /** + * Returns a list of components from all the disks. We assume + * the components are stored within the components directory. + * + * Also, we treat all in-memory templates as components. + * + * The return path is same the path you will pass to the `@component` + * tag. + */ + listComponents(): ComponentsTree { + const diskNames = [...this.#mountedDirs.keys()] + return diskNames.map((diskName) => { + return { + diskName, + components: this.#getDiskComponents(diskName), + } + }) + } + + /** + * Returns a list of templates from all the disks and in-memory + * templates as well + */ + listTemplates(): { diskName: string; templates: string[] }[] { + const diskNames = [...this.#mountedDirs.keys()] + return diskNames.map((diskName) => { + return { + diskName, + templates: this.#getDiskTemplates(diskName), + } + }) + } +} diff --git a/src/Edge/globals/index.ts b/src/migrate/globals.ts similarity index 81% rename from src/Edge/globals/index.ts rename to src/migrate/globals.ts index e7b2943..e3f1e17 100644 --- a/src/Edge/globals/index.ts +++ b/src/migrate/globals.ts @@ -1,16 +1,21 @@ /* * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ import { EdgeError } from 'edge-error' +// @ts-ignore untyped module import stringify from 'js-stringify' -import { string } from '@poppinss/utils/build/helpers' -import { safeValue, escape } from '../../Template' +// @ts-ignore untyped module +import inspect from '@poppinss/inspect' +const { string: prettyPrintHtml } = inspect +import string from '@poppinss/utils/string' + +import { htmlSafe, escape } from '../template.js' export const GLOBALS = { /** @@ -28,7 +33,7 @@ export const GLOBALS = { * Inspect state */ inspect: (value: any) => { - return safeValue(require('@poppinss/inspect').string.html(value)) + return htmlSafe(prettyPrintHtml.html(value)) }, /** @@ -85,7 +90,7 @@ export const GLOBALS = { * whereas this method converts it to an actual instance of date */ stringify: stringify, - safe: safeValue, + safe: htmlSafe, camelCase: string.camelCase, snakeCase: string.snakeCase, dashCase: string.dashCase, @@ -96,10 +101,10 @@ export const GLOBALS = { noCase: string.noCase, titleCase: string.titleCase, pluralize: string.pluralize, - toSentence: string.toSentence, - prettyBytes: string.prettyBytes, - toBytes: string.toBytes, - prettyMs: string.prettyMs, - toMs: string.toMs, - ordinalize: string.ordinalize, -} + sentence: string.sentence, + prettyMs: string.milliseconds.format, + toMs: string.milliseconds.parse, + prettyBytes: string.bytes.format, + toBytes: string.bytes.parse, + ordinal: string.ordinal, +} as Record diff --git a/src/migrate/plugin.ts b/src/migrate/plugin.ts new file mode 100644 index 0000000..c453ce6 --- /dev/null +++ b/src/migrate/plugin.ts @@ -0,0 +1,24 @@ +/* + * edge.js + * + * (c) Edge + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import * as Tags from './tags/main.js' +import { GLOBALS } from './globals.js' +import type { PluginFn } from '../types.js' + +export const migrate: PluginFn = (edge) => { + edge.compat = true + edge.compiler.compat = true + edge.asyncCompiler.compat = true + Object.keys(GLOBALS).forEach((name) => { + edge.global(name, GLOBALS[name]) + }) + Object.keys(Tags).forEach((name) => { + edge.registerTag(Tags[name as keyof typeof Tags]) + }) +} diff --git a/src/Component/Props.ts b/src/migrate/props.ts similarity index 61% rename from src/Component/Props.ts rename to src/migrate/props.ts index 88e449c..5168a84 100644 --- a/src/Component/Props.ts +++ b/src/migrate/props.ts @@ -1,31 +1,39 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { lodash } from '@poppinss/utils' +import lodash from '@poppinss/utils/lodash' import stringifyAttributes from 'stringify-attributes' -import { safeValue } from '../Template' -import { PropsContract } from '../Contracts' +import { htmlSafe } from '../template.js' +import { ComponentProps } from '../component/props.js' /** * Class to ease interactions with component props */ -export class Props implements PropsContract { +export class Props { + /** + * Use ".next" property to migrate to newer + * api + */ + next: ComponentProps + constructor(props: any) { + // @ts-ignore this[Symbol.for('options')] = { props } Object.assign(this, props) + this.next = new ComponentProps(props) } /** * Merges the className attribute with the class attribute */ - private mergeClassAttributes(props: any) { + #mergeClassAttributes(props: any) { if (props.className) { if (!props.class) { props.class = [] @@ -48,7 +56,7 @@ export class Props implements PropsContract { /** * Find if a key exists inside the props */ - public has(key: string) { + has(key: string) { const value = this.get(key) return value !== undefined && value !== null } @@ -56,21 +64,22 @@ export class Props implements PropsContract { /** * Get value for a given key */ - public get(key: string, defaultValue?: any) { + get(key: string, defaultValue?: any) { return lodash.get(this.all(), key, defaultValue) } /** * Returns all the props */ - public all() { + all() { + // @ts-ignore return this[Symbol.for('options')].props } /** * Validate prop value */ - public validate(key: string, validateFn: (key: string, value?: any) => any) { + validate(key: string, validateFn: (key: string, value?: any) => any) { const value = this.get(key) validateFn(key, value) } @@ -78,56 +87,56 @@ export class Props implements PropsContract { /** * Return values for only the given keys */ - public only(keys: string[]) { + only(keys: string[]) { return lodash.pick(this.all(), keys) } /** * Return values except the given keys */ - public except(keys: string[]) { + except(keys: string[]) { return lodash.omit(this.all(), keys) } /** * Serialize all props to a string of HTML attributes */ - public serialize(mergeProps?: any, priortizeInline: boolean = true) { + serialize(mergeProps?: any, prioritizeInline: boolean = true) { /** - * Priortize user attributes when priortizeInline=false + * Prioritize user attributes when prioritizeInline=false */ - const attributes = priortizeInline + const attributes = prioritizeInline ? lodash.merge({}, this.all(), mergeProps) : lodash.merge({}, mergeProps, this.all()) - return safeValue(stringifyAttributes(this.mergeClassAttributes(attributes))) + return htmlSafe(stringifyAttributes(this.#mergeClassAttributes(attributes))) } /** * Serialize only the given keys to a string of HTML attributes */ - public serializeOnly(keys: string[], mergeProps?: any, priortizeInline: boolean = true) { + serializeOnly(keys: string[], mergeProps?: any, prioritizeInline: boolean = true) { /** - * Priortize user attributes when priortizeInline=false + * Prioritize user attributes when prioritizeInline=false */ - const attributes = priortizeInline + const attributes = prioritizeInline ? lodash.merge({}, this.only(keys), mergeProps) : lodash.merge({}, mergeProps, this.only(keys)) - return safeValue(stringifyAttributes(this.mergeClassAttributes(attributes))) + return htmlSafe(stringifyAttributes(this.#mergeClassAttributes(attributes))) } /** * Serialize except the given keys to a string of HTML attributes */ - public serializeExcept(keys: string[], mergeProps?: any, priortizeInline: boolean = true) { + serializeExcept(keys: string[], mergeProps?: any, prioritizeInline: boolean = true) { /** - * Priortize user attributes when priortizeInline=false + * Prioritize user attributes when prioritizeInline=false */ - const attributes = priortizeInline + const attributes = prioritizeInline ? lodash.merge({}, this.except(keys), mergeProps) : lodash.merge({}, mergeProps, this.except(keys)) - return safeValue(stringifyAttributes(this.mergeClassAttributes(attributes))) + return htmlSafe(stringifyAttributes(this.#mergeClassAttributes(attributes))) } } diff --git a/src/Tags/Layout.ts b/src/migrate/tags/layout.ts similarity index 85% rename from src/Tags/Layout.ts rename to src/migrate/tags/layout.ts index 80a4975..3c21dd8 100644 --- a/src/Tags/Layout.ts +++ b/src/migrate/tags/layout.ts @@ -1,13 +1,13 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { TagContract } from '../Contracts' +import { TagContract } from '../../types.js' /** * Layout tag is used to define parent layout for a given template. The layout diff --git a/src/migrate/tags/main.ts b/src/migrate/tags/main.ts new file mode 100644 index 0000000..f546de8 --- /dev/null +++ b/src/migrate/tags/main.ts @@ -0,0 +1,13 @@ +/* + * edge.js + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +export { setTag as set } from './set.js' +export { superTag as super } from './super.js' +export { layoutTag as layout } from './layout.js' +export { sectionTag as section } from './section.js' diff --git a/src/Tags/Section.ts b/src/migrate/tags/section.ts similarity index 84% rename from src/Tags/Section.ts rename to src/migrate/tags/section.ts index 1c6d5e7..60c7eec 100644 --- a/src/Tags/Section.ts +++ b/src/migrate/tags/section.ts @@ -1,13 +1,13 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { TagContract } from '../Contracts' +import { TagContract } from '../../types.js' /** * Section tag is used to define the sections on a given template. Sections cannot be diff --git a/src/Tags/Set.ts b/src/migrate/tags/set.ts similarity index 84% rename from src/Tags/Set.ts rename to src/migrate/tags/set.ts index 752f08c..8b3866d 100644 --- a/src/Tags/Set.ts +++ b/src/migrate/tags/set.ts @@ -1,7 +1,7 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -9,10 +9,16 @@ import { EdgeError } from 'edge-error' import { expressions } from 'edge-parser' -import { lodash } from '@poppinss/utils' +import lodash from '@poppinss/utils/lodash' -import { TagContract } from '../Contracts' -import { isSubsetOf, unallowedExpression, parseJsArg } from '../utils' +import { TagContract } from '../../types.js' +import { isSubsetOf, unallowedExpression, parseJsArg } from '../../utils.js' + +declare module '../../template.js' { + export interface Template { + setValue: (typeof lodash)['set'] + } +} /** * The set tag is used to set runtime values within the template. The value @@ -56,7 +62,8 @@ export const setTag: TagContract = { }) /** - * Disallow more than 2 values for the sequence expression + * Disallow less than 2 and more than 3 values for the sequence + * expression */ if (parsed.expressions.length < 2 || parsed.expressions.length > 3) { throw new EdgeError( @@ -86,17 +93,6 @@ export const setTag: TagContract = { value = parsed.expressions[1] } - /** - * The key has to be a literal value - */ - isSubsetOf(key, [expressions.Literal], () => { - throw unallowedExpression( - `The ${collection ? 'second' : 'first'} argument for @set tag must be a string literal`, - token.filename, - parser.utils.getExpressionLoc(key) - ) - }) - /** * Mutate the collection when defined */ diff --git a/src/Tags/Super.ts b/src/migrate/tags/super.ts similarity index 89% rename from src/Tags/Super.ts rename to src/migrate/tags/super.ts index 8a24a9a..dacd8c2 100644 --- a/src/Tags/Super.ts +++ b/src/migrate/tags/super.ts @@ -1,14 +1,14 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ import { EdgeError } from 'edge-error' -import { TagContract } from '../Contracts' +import { TagContract } from '../../types.js' /** * Super tag is used inside sections to inherit the parent section diff --git a/src/plugins/supercharged.ts b/src/plugins/supercharged.ts new file mode 100644 index 0000000..423348f --- /dev/null +++ b/src/plugins/supercharged.ts @@ -0,0 +1,94 @@ +/* + * edge.js + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { Edge } from '../edge/main.js' +import { PluginFn } from '../types.js' + +/** + * Hooks into the compiler phase of Edge and converts + * tags to components. + * + * The components are discovered from the components directory + * inside every registered disk. + */ +class SuperChargedComponents { + #edge: Edge + #components: Record = {} + + constructor(edge: Edge) { + this.#edge = edge + this.#claimTags() + this.#transformTags() + } + + /** + * Refreshes the list of components + */ + refreshComponents() { + this.#components = this.#edge.loader + .listComponents() + .reduce>((result, { components }) => { + components.forEach((component) => { + result[component.tagName] = component.componentName + }) + return result + }, {}) + } + + /** + * Registers hook to claim self processing of tags that + * are references to components + */ + #claimTags() { + this.#edge.compiler.claimTag((name) => { + if (this.#components[name]) { + return { seekable: true, block: true } + } + return null + }) + + this.#edge.asyncCompiler.claimTag((name) => { + if (this.#components[name]) { + return { seekable: true, block: true } + } + return null + }) + } + + /** + * Transforms tags to component calls + */ + #transformTags() { + this.#edge.processor.process('tag', ({ tag }) => { + const component = this.#components[tag.properties.name] + if (!component) { + return + } + + tag.properties.name = 'component' + if (tag.properties.jsArg.trim() === '') { + tag.properties.jsArg = `'${component}'` + } else { + tag.properties.jsArg = `'${component}',${tag.properties.jsArg}` + } + }) + } +} + +/** + * The superCharged plugin converts components stored within the + * components directory of all the disk to Edge tags. + */ +let superCharged: SuperChargedComponents +export const pluginSuperCharged: PluginFn<{ recurring: boolean }> = (edge, firstRun) => { + if (firstRun) { + superCharged = new SuperChargedComponents(edge) + } + superCharged.refreshComponents() +} diff --git a/src/Processor/index.ts b/src/processor.ts similarity index 54% rename from src/Processor/index.ts rename to src/processor.ts index 0f1f15f..bd946cc 100644 --- a/src/Processor/index.ts +++ b/src/processor.ts @@ -1,27 +1,27 @@ /* - * edge-js + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { TagToken } from 'edge-lexer' -import { ProcessorContract, TemplateContract } from '../Contracts' +import type { Template } from './template.js' +import type { TagToken } from 'edge-lexer/types' /** * Exposes the API to register a set of handlers to process the * templates output at different stages */ -export class Processor implements ProcessorContract { - private handlers: Map any>> = new Map() +export class Processor { + #handlers: Map any>> = new Map() /** * Execute tag handler */ - public executeTag(data: { tag: TagToken; path: string }): void { - const handlers = this.handlers.get('tag') + executeTag(data: { tag: TagToken; path: string }): void { + const handlers = this.#handlers.get('tag') if (!handlers) { return } @@ -34,8 +34,8 @@ export class Processor implements ProcessorContract { /** * Execute raw handlers */ - public executeRaw(data: { raw: string; path: string }): string { - const handlers = this.handlers.get('raw') + executeRaw(data: { raw: string; path: string }): string { + const handlers = this.#handlers.get('raw') if (!handlers) { return data.raw } @@ -53,8 +53,8 @@ export class Processor implements ProcessorContract { /** * Execute compiled handlers */ - public executeCompiled(data: { compiled: string; path: string }): string { - const handlers = this.handlers.get('compiled') + executeCompiled(data: { compiled: string; path: string }): string { + const handlers = this.#handlers.get('compiled') if (!handlers) { return data.compiled } @@ -72,12 +72,8 @@ export class Processor implements ProcessorContract { /** * Execute output handlers */ - public executeOutput(data: { - output: string - template: TemplateContract - state: Record - }): string { - const handlers = this.handlers.get('output') + executeOutput(data: { output: string; template: Template; state: Record }): string { + const handlers = this.#handlers.get('output') if (!handlers) { return data.output } @@ -95,29 +91,26 @@ export class Processor implements ProcessorContract { /** * Define a processor function */ - public process( - event: 'raw', - handler: (data: { raw: string; path: string }) => string | void - ): this - public process(event: 'tag', handler: (data: { tag: TagToken; path: string }) => void): this - public process( + process(event: 'raw', handler: (data: { raw: string; path: string }) => string | void): this + process(event: 'tag', handler: (data: { tag: TagToken; path: string }) => void): this + process( event: 'compiled', handler: (data: { compiled: string; path: string }) => string | void ): this - public process( + process( event: 'output', handler: (data: { output: string - template: TemplateContract + template: Template state: Record }) => string | void ): this - public process(event: string, handler: (...args: any[]) => any): this { - if (!this.handlers.has(event)) { - this.handlers.set(event, new Set()) + process(event: string, handler: (...args: any[]) => any): this { + if (!this.#handlers.has(event)) { + this.#handlers.set(event, new Set()) } - this.handlers.get(event)!.add(handler) + this.#handlers.get(event)!.add(handler) return this } } diff --git a/src/tags/assign.ts b/src/tags/assign.ts new file mode 100644 index 0000000..db5455d --- /dev/null +++ b/src/tags/assign.ts @@ -0,0 +1,48 @@ +/* + * edge.js + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { expressions } from 'edge-parser' +import lodash from '@poppinss/utils/lodash' + +import { TagContract } from '../types.js' +import { isSubsetOf, parseJsArg, unallowedExpression } from '../utils.js' + +/** + * The assign tag is used to re-assign value to an existing variable + */ +export const assignTag: TagContract = { + block: false, + seekable: true, + tagName: 'assign', + noNewLine: true, + + /** + * Compiles else block node to Javascript else statement + */ + compile(parser, buffer, token) { + const parsed = parseJsArg(parser, token) + + isSubsetOf(parsed, [expressions.AssignmentExpression], () => { + throw unallowedExpression( + `Invalid expression for the @assign tag`, + token.filename, + parser.utils.getExpressionLoc(parsed) + ) + }) + + buffer.writeExpression(parser.utils.stringify(parsed), token.filename, token.loc.start.line) + }, + + /** + * Add methods to the template for running the loop + */ + boot(template) { + template.macro('setValue', lodash.set) + }, +} diff --git a/src/Tags/Component.ts b/src/tags/component.ts similarity index 94% rename from src/Tags/Component.ts rename to src/tags/component.ts index eb97560..11fa990 100644 --- a/src/Tags/Component.ts +++ b/src/tags/component.ts @@ -1,19 +1,19 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ import { EdgeError } from 'edge-error' -import { TagToken, utils as lexerUtils } from 'edge-lexer' +import * as lexerUtils from 'edge-lexer/utils' +import type { TagToken } from 'edge-lexer/types' import { EdgeBuffer, expressions, Parser } from 'edge-parser' -import { TagContract } from '../Contracts' -import { StringifiedObject } from '../StringifiedObject' -import { isSubsetOf, unallowedExpression, parseJsArg } from '../utils' +import type { TagContract } from '../types.js' +import { isSubsetOf, unallowedExpression, parseJsArg, StringifiedObject } from '../utils.js' /** * A list of allowed expressions for the component name @@ -80,19 +80,6 @@ function getComponentNameAndProps( * expression, as components allows a max of two arguments */ const firstSequenceExpression = expression.expressions[0] - - if ( - firstSequenceExpression && - [expressions.ObjectExpression, expressions.AssignmentExpression].includes( - firstSequenceExpression.type - ) - ) { - return [ - parser.utils.stringify(name), - StringifiedObject.fromAcornExpressions([firstSequenceExpression], parser), - ] - } - return [parser.utils.stringify(name), parser.utils.stringify(firstSequenceExpression)] } diff --git a/src/Tags/Debugger.ts b/src/tags/debugger.ts similarity index 83% rename from src/Tags/Debugger.ts rename to src/tags/debugger.ts index ea17f07..4b7fbd9 100644 --- a/src/Tags/Debugger.ts +++ b/src/tags/debugger.ts @@ -1,13 +1,13 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { TagContract } from '../Contracts' +import type { TagContract } from '../types.js' /** * Add debugger break point to the compiled template diff --git a/src/Tags/Each.ts b/src/tags/each.ts similarity index 94% rename from src/Tags/Each.ts rename to src/tags/each.ts index f9d1035..6d48a46 100644 --- a/src/Tags/Each.ts +++ b/src/tags/each.ts @@ -1,18 +1,26 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { lodash } from '@poppinss/utils' -import { utils as lexerUtils } from 'edge-lexer' +import lodash from '@poppinss/utils/lodash' +import * as lexerUtils from 'edge-lexer/utils' import { Parser, expressions } from 'edge-parser' -import { TagContract } from '../Contracts' -import { isSubsetOf, asyncEach, each, unallowedExpression } from '../utils' +import { TagContract } from '../types.js' +import { isSubsetOf, asyncEach, each, unallowedExpression } from '../utils.js' + +declare module '../template.js' { + export interface Template { + loopAsync: typeof asyncEach + loop: typeof each + size: (typeof lodash)['size'] + } +} /** * Returns the list to loop over for the each binary expression diff --git a/src/Tags/Else.ts b/src/tags/else.ts similarity index 81% rename from src/Tags/Else.ts rename to src/tags/else.ts index ad2f8c0..dbdef85 100644 --- a/src/Tags/Else.ts +++ b/src/tags/else.ts @@ -1,13 +1,13 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { TagContract } from '../Contracts' +import { TagContract } from '../types.js' export const elseTag: TagContract = { block: false, diff --git a/src/Tags/ElseIf.ts b/src/tags/else_if.ts similarity index 92% rename from src/Tags/ElseIf.ts rename to src/tags/else_if.ts index 184fbbf..a2e2f2c 100644 --- a/src/Tags/ElseIf.ts +++ b/src/tags/else_if.ts @@ -1,15 +1,15 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ import { expressions } from 'edge-parser' -import { TagContract } from '../Contracts' -import { isNotSubsetOf, unallowedExpression, parseJsArg } from '../utils' +import { TagContract } from '../types.js' +import { isNotSubsetOf, unallowedExpression, parseJsArg } from '../utils.js' /** * Else if tag is used to define conditional blocks. We keep `@elseif` tag diff --git a/src/tags/eval.ts b/src/tags/eval.ts new file mode 100644 index 0000000..4109355 --- /dev/null +++ b/src/tags/eval.ts @@ -0,0 +1,31 @@ +/* + * edge.js + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { parseJsArg } from '../utils.js' +import type { TagContract } from '../types.js' + +/** + * The eval tag accepts expressions similar to double curly + * braces. However, it does not write anything to the + * output. + */ +export const evalTag: TagContract = { + block: false, + seekable: true, + tagName: 'eval', + noNewLine: true, + + /** + * Compiles the tag AST + */ + compile(parser, buffer, token) { + const parsed = parseJsArg(parser, token) + buffer.writeExpression(parser.utils.stringify(parsed), token.filename, token.loc.start.line) + }, +} diff --git a/src/Tags/If.ts b/src/tags/if.ts similarity index 92% rename from src/Tags/If.ts rename to src/tags/if.ts index f0d7dbe..b2f3aca 100644 --- a/src/Tags/If.ts +++ b/src/tags/if.ts @@ -1,7 +1,7 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -9,8 +9,8 @@ import { expressions } from 'edge-parser' -import { TagContract } from '../Contracts' -import { unallowedExpression, isNotSubsetOf, parseJsArg } from '../utils' +import { TagContract } from '../types.js' +import { unallowedExpression, isNotSubsetOf, parseJsArg } from '../utils.js' /** * If tag is used to define conditional blocks. diff --git a/src/Tags/Include.ts b/src/tags/include.ts similarity index 96% rename from src/Tags/Include.ts rename to src/tags/include.ts index 9e20999..b83b8d1 100644 --- a/src/Tags/Include.ts +++ b/src/tags/include.ts @@ -1,7 +1,7 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) AdonisJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -9,20 +9,20 @@ import { expressions, Parser } from 'edge-parser' -import { TagContract } from '../Contracts' -import { unallowedExpression, isSubsetOf, parseJsArg } from '../utils' +import type { TagContract } from '../types.js' +import { unallowedExpression, isSubsetOf, parseJsArg } from '../utils.js' /** * List of expressions allowed for the include tag */ export const ALLOWED_EXPRESSION = [ - expressions.Identifier, expressions.Literal, - expressions.LogicalExpression, - expressions.MemberExpression, - expressions.ConditionalExpression, + expressions.Identifier, expressions.CallExpression, expressions.TemplateLiteral, + expressions.MemberExpression, + expressions.LogicalExpression, + expressions.ConditionalExpression, ] /** diff --git a/src/Tags/IncludeIf.ts b/src/tags/include_if.ts similarity index 92% rename from src/Tags/IncludeIf.ts rename to src/tags/include_if.ts index 8c16764..a6c3dd0 100644 --- a/src/Tags/IncludeIf.ts +++ b/src/tags/include_if.ts @@ -1,7 +1,7 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -10,9 +10,9 @@ import { EdgeError } from 'edge-error' import { expressions } from 'edge-parser' -import { TagContract } from '../Contracts' -import { ALLOWED_EXPRESSION, getRenderExpression } from './Include' -import { unallowedExpression, isSubsetOf, parseJsArg, isNotSubsetOf } from '../utils' +import { TagContract } from '../types.js' +import { ALLOWED_EXPRESSION, getRenderExpression } from './include.js' +import { unallowedExpression, isSubsetOf, parseJsArg, isNotSubsetOf } from '../utils.js' /** * Include tag is used to include partials in the same scope of the parent diff --git a/src/Tags/Inject.ts b/src/tags/inject.ts similarity index 74% rename from src/Tags/Inject.ts rename to src/tags/inject.ts index 71e9809..b3dafba 100644 --- a/src/Tags/Inject.ts +++ b/src/tags/inject.ts @@ -1,7 +1,7 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -9,8 +9,8 @@ import { expressions } from 'edge-parser' -import { TagContract } from '../Contracts' -import { isSubsetOf, unallowedExpression, parseJsArg } from '../utils' +import type { TagContract } from '../types.js' +import { isSubsetOf, unallowedExpression, parseJsArg } from '../utils.js' /** * The inject tag is used within the components to share values with the @@ -29,13 +29,17 @@ export const injectTag: TagContract = { /** * The inject tag only accepts an object expression. */ - isSubsetOf(parsed, [expressions.ObjectExpression, expressions.Identifier], () => { - throw unallowedExpression( - `"${token.properties.jsArg}" is not a valid key-value pair for the @inject tag`, - token.filename, - parser.utils.getExpressionLoc(parsed) - ) - }) + isSubsetOf( + parsed, + [expressions.ObjectExpression, expressions.Identifier, expressions.CallExpression], + () => { + throw unallowedExpression( + `"${token.properties.jsArg}" is not a valid key-value pair for the @inject tag`, + token.filename, + parser.utils.getExpressionLoc(parsed) + ) + } + ) /** * Ensure $slots are defined before merging shared state diff --git a/src/tags/let.ts b/src/tags/let.ts new file mode 100644 index 0000000..5cb83ad --- /dev/null +++ b/src/tags/let.ts @@ -0,0 +1,84 @@ +/* + * edge.js + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { expressions } from 'edge-parser' +import lodash from '@poppinss/utils/lodash' + +import { TagContract } from '../types.js' +import { isSubsetOf, unallowedExpression } from '../utils.js' + +/** + * The let tag is used to set runtime values within the template. The value + * is set inside the current scope of the template. + */ +export const letTag: TagContract = { + block: false, + seekable: true, + tagName: 'let', + noNewLine: true, + + /** + * Compiles else block node to Javascript else statement + */ + compile(parser, buffer, token) { + const parsed = parser.utils.generateAST( + `let ${token.properties.jsArg}`, + token.loc, + token.filename + ).declarations[0] + + const key = parsed.id + const value = parsed.init + + /** + * The variable name has to be an identifier or the destructuring + * operator. + */ + isSubsetOf(key, ['ObjectPattern', expressions.Identifier, 'ArrayPattern'], () => { + throw unallowedExpression( + `Invalid variable name for the @let tag`, + token.filename, + parser.utils.getExpressionLoc(key) + ) + }) + + /** + * Define local variables based upon the expression + */ + if (key.type === 'Identifier') { + parser.stack.defineVariable(key.name) + } else if (key.type === 'ObjectPattern') { + key.properties.forEach((property: any) => { + parser.stack.defineVariable( + property.argument ? property.argument.name : property.value.name + ) + }) + } else if (key.type === 'ArrayPattern') { + key.elements.forEach((element: any) => { + parser.stack.defineVariable(element.argument ? element.argument.name : element.name) + }) + } + + /** + * Declare let variable + */ + const expression = `let ${parser.utils.stringify(key)} = ${parser.utils.stringify( + parser.utils.transformAst(value, token.filename, parser) + )}` + + buffer.writeExpression(expression, token.filename, token.loc.start.line) + }, + + /** + * Add methods to the template for running the loop + */ + boot(template) { + template.macro('setValue', lodash.set) + }, +} diff --git a/src/tags/main.ts b/src/tags/main.ts new file mode 100644 index 0000000..da7ee26 --- /dev/null +++ b/src/tags/main.ts @@ -0,0 +1,24 @@ +/* + * edge.js + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +export { ifTag as if } from './if.js' +export { letTag as let } from './let.js' +export { eachTag as each } from './each.js' +export { slotTag as slot } from './slot.js' +export { elseTag as else } from './else.js' +export { evalTag as eval } from './eval.js' +export { assignTag as assign } from './assign.js' +export { injectTag as inject } from './inject.js' +export { unlessTag as unless } from './unless.js' +export { elseIfTag as elseif } from './else_if.js' +export { includeTag as include } from './include.js' +export { debuggerTag as debugger } from './debugger.js' +export { newErrorTag as newError } from './new_error.js' +export { componentTag as component } from './component.js' +export { includeIfTag as includeIf } from './include_if.js' diff --git a/src/Tags/NewError.ts b/src/tags/new_error.ts similarity index 91% rename from src/Tags/NewError.ts rename to src/tags/new_error.ts index 661eaa5..eb78469 100644 --- a/src/Tags/NewError.ts +++ b/src/tags/new_error.ts @@ -1,7 +1,7 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -9,8 +9,8 @@ import { expressions } from 'edge-parser' -import { TagContract } from '../Contracts' -import { parseJsArg } from '../utils' +import { TagContract } from '../types.js' +import { parseJsArg } from '../utils.js' /** * newError tag to raise exceptions inside your templates. They will point diff --git a/src/Tags/Slot.ts b/src/tags/slot.ts similarity index 88% rename from src/Tags/Slot.ts rename to src/tags/slot.ts index 77dcd7c..59f5616 100644 --- a/src/Tags/Slot.ts +++ b/src/tags/slot.ts @@ -1,14 +1,14 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ import { EdgeError } from 'edge-error' -import { TagContract } from '../Contracts' +import { TagContract } from '../types.js' /** * Slot tag is used to define the slots of a given component. Slots cannot be diff --git a/src/Tags/Unless.ts b/src/tags/unless.ts similarity index 93% rename from src/Tags/Unless.ts rename to src/tags/unless.ts index 1d38003..d078638 100644 --- a/src/Tags/Unless.ts +++ b/src/tags/unless.ts @@ -1,7 +1,7 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -9,8 +9,8 @@ import { expressions } from 'edge-parser' -import { TagContract } from '../Contracts' -import { isNotSubsetOf, unallowedExpression, parseJsArg } from '../utils' +import { TagContract } from '../types.js' +import { isNotSubsetOf, unallowedExpression, parseJsArg } from '../utils.js' /** * Inverse of the `if` condition. The term `unless` is more readable and logical diff --git a/src/template.ts b/src/template.ts new file mode 100644 index 0000000..f3ce1b2 --- /dev/null +++ b/src/template.ts @@ -0,0 +1,207 @@ +/* + * edge.js + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import he from 'he' +import { EdgeError } from 'edge-error' +import lodash from '@poppinss/utils/lodash' +import Macroable from '@poppinss/macroable' + +import { Compiler } from './compiler.js' +import { Processor } from './processor.js' +import { Props } from './migrate/props.js' +import type { CompiledTemplate } from './types.js' +import { ComponentProps } from './component/props.js' + +/** + * An instance of this class passed to the escape + * method ensures that underlying value is never + * escaped. + */ +class SafeValue { + constructor(public value: any) {} +} + +/** + * Escapes a given string + */ +export function escape(input: any): string { + return input instanceof SafeValue ? input.value : he.escape(String(input)) +} + +/** + * Mark value as safe and not to be escaped + */ +export function htmlSafe(value: string) { + return new SafeValue(value) +} + +/** + * The template is used to compile and run templates. Also the instance + * of template is passed during runtime to render `dynamic partials` + * and `dynamic components`. + */ +export class Template extends Macroable { + #compiler: Compiler + #processor: Processor + + /** + * The shared state is used to hold the globals and locals, + * since it is shared with components too. + */ + #sharedState: Record + + constructor(compiler: Compiler, globals: any, locals: any, processor: Processor) { + super() + this.#compiler = compiler + this.#processor = processor + this.#sharedState = compiler.compat + ? lodash.merge({}, globals, locals) + : { + ...globals, + ...locals, + } + } + + /** + * Trims top and bottom new lines from the content + */ + #trimTopBottomNewLines(value: string) { + return value.replace(/^\n|^\r\n/, '').replace(/\n$|\r\n$/, '') + } + + /** + * Render a compiled template with state + */ + #renderCompiled(compiledTemplate: CompiledTemplate, state: any) { + const templateState = { ...this.#sharedState, ...state } + const $context = {} + + /** + * Process template as a promise. + */ + if (this.#compiler.async) { + return compiledTemplate(this, templateState, $context).then((output: string) => { + output = this.#trimTopBottomNewLines(output) + return this.#processor.executeOutput({ output, template: this, state: templateState }) + }) + } + + const output = this.#trimTopBottomNewLines(compiledTemplate(this, templateState, $context)) + return this.#processor.executeOutput({ output, template: this, state: templateState }) + } + + /** + * Render a partial + * + * ```js + * const partialFn = template.compilePartial('includes/user') + * + * // render and use output + * partialFn(template, state, ctx) + * ``` + */ + compilePartial(templatePath: string, ...localVariables: string[]): CompiledTemplate { + return this.#compiler.compile(templatePath, localVariables) + } + + /** + * Render a component + * + * ```js + * const componentFn = template.compileComponent('components/button') + * + * // render and use output + * componentFn(template, template.getComponentState(props, slots, caller), ctx) + * ``` + */ + compileComponent(templatePath: string): CompiledTemplate { + return this.#compiler.compile(templatePath) + } + + /** + * Returns the isolated state for a given component + */ + getComponentState( + props: { [key: string]: any }, + slots: { [key: string]: any }, + caller: { filename: string; line: number; col: number } + ) { + return { + ...this.#sharedState, + ...props, + $slots: slots, + $caller: caller, + $props: this.#compiler.compat ? new Props(props) : new ComponentProps(props), + } + } + + /** + * Render a template with it's state. + * + * ```js + * template.render('welcome', { key: 'value' }) + * ``` + */ + render | string>(template: string, state: any): T { + let compiledTemplate = this.#compiler.compile(template) + return this.#renderCompiled(compiledTemplate, state) + } + + /** + * Render template from a raw string + * + * ```js + * template.renderRaw('Hello {{ username }}', { username: 'virk' }) + * ``` + */ + renderRaw | string>( + contents: string, + state: any, + templatePath?: string + ): T { + let compiledTemplate = this.#compiler.compileRaw(contents, templatePath) + return this.#renderCompiled(compiledTemplate, state) + } + + /** + * Escapes the value to be HTML safe. Only strings are escaped + * and rest all values will be returned as it is. + */ + escape(input: any): string { + return escape(input) + } + + /** + * Raise an error + */ + newError(errorMessage: string, filename: string, lineNumber: number, column: number) { + throw new EdgeError(errorMessage, 'E_RUNTIME_EXCEPTION', { + filename: filename, + line: lineNumber, + col: column, + }) + } + + /** + * Rethrows the runtime exception by re-constructing the error message + * to point back to the original filename + */ + reThrow(error: any, filename: string, lineNumber: number): never { + if (error instanceof EdgeError) { + throw error + } + + const message = error.message.replace(/state\./, '') + throw new EdgeError(message, 'E_RUNTIME_EXCEPTION', { + filename: filename, + line: lineNumber, + col: 0, + }) + } +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..b7db191 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,163 @@ +/** + * edge + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import type { TagToken } from 'edge-lexer/types' +import type { Parser, EdgeBuffer } from 'edge-parser' +import type { ParserTagDefinitionContract } from 'edge-parser/types' + +import type { Edge } from './edge/main.js' +import type { Template } from './template.js' + +/** + * The shape in which the loader must resolve the template + */ +export type LoaderTemplate = { + template: string +} + +export type ComponentsTree = { + diskName: string + components: { + componentName: string + tagName: string + }[] +}[] + +/** + * Loader contract that every loader must adheres to. + */ +export interface LoaderContract { + /** + * List of mounted disks + */ + mounted: { [diskName: string]: string } + + /** + * List of pre-registered template + */ + templates: { [templatePath: string]: LoaderTemplate } + + /** + * Save disk name and dirPath to resolve views + */ + mount(diskName: string, dirPath: string | URL): void + + /** + * Remove disk from the previously saved paths + */ + unmount(diskName: string): void + + /** + * Resolve template contents + */ + resolve(templatePath: string): LoaderTemplate + + /** + * Make absolute path to a template + */ + makePath(templatePath: string): string + + /** + * Register in memory template and presenter + */ + register(templatePath: string, contents: LoaderTemplate): void + + /** + * Remove the pre-registered template + */ + remove(templatePath: string): void + + /** + * Returns a list of components for all the registered disks + */ + listComponents(): ComponentsTree +} + +/** + * The tag must have a tagName along with other properties + * required by lexer and parser + */ +export interface TagContract extends ParserTagDefinitionContract { + tagName: string + boot?(template: typeof Template): void +} + +/** + * Shape of collection of tags + */ +export type TagsContract = { + [tagName: string]: TagContract +} + +/** + * Shape of compiled template as a function + */ +export type CompiledTemplate = ( + template: Template, + state: Record, + $context: Record | undefined, + ...localVariables: any[] +) => any + +/** + * Shape of the cache manager + */ +export interface CacheManagerContract { + enabled: boolean + get(templatePath: string): undefined | CompiledTemplate + set(templatePath: string, compiledOutput: CompiledTemplate): void + has(templatePath: string): boolean + delete(templatePath: string): void +} + +/** + * Compiler constructor options + */ +export type CompilerOptions = { + cache?: boolean + async?: boolean + compat?: boolean +} + +/** + * Shape of options that can be passed to the + * edge constructor + */ +export type EdgeOptions = { + loader?: LoaderContract + cache?: boolean +} + +/** + * Shape of edge plugin + */ +export type PluginFn = (edge: Edge, firstRun: boolean, options: T) => void + +/** + * Shape of global helpers + */ +export type EdgeGlobals = Record + +/** + * Required when creating custom tags + */ +export type ParserContract = Parser +export type TagTokenContract = TagToken +export type EdgeBufferContract = EdgeBuffer + +export type * from 'edge-lexer/types' +export type { + AcornLoc, + ClaimTagFn, + MustacheTransformer, + OnLineFn, + ParserOptions, + ParserTagDefinitionContract, + TagTransformer, +} from 'edge-parser/types' diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..545e15d --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,318 @@ +/* + * edge.js + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import classNames from 'classnames' +import { EdgeError } from 'edge-error' +import type { TagToken } from 'edge-lexer/types' +import { find, html } from 'property-information' +import { expressions as expressionsList, Parser } from 'edge-parser' + +type ExpressionList = readonly (keyof typeof expressionsList | 'ObjectPattern' | 'ArrayPattern')[] + +/** + * Function to register custom properties + * with "property-information" package. + */ +function definePropertyInformation(property: string, value?: any) { + html.normal[property] = property + html.property[property] = { + attribute: property, + boolean: true, + property: property, + space: 'html', + booleanish: false, + commaOrSpaceSeparated: false, + commaSeparated: false, + spaceSeparated: false, + number: false, + overloadedBoolean: false, + defined: false, + mustUseProperty: false, + ...value, + } +} + +definePropertyInformation('x-cloak') +definePropertyInformation('x-ignore') +definePropertyInformation('x-transition:enterstart', { + attribute: 'x-transition:enter-start', + property: 'x-transition:enterStart', + boolean: false, + spaceSeparated: true, + commaOrSpaceSeparated: true, +}) +definePropertyInformation('x-transition:enterend', { + attribute: 'x-transition:enter-end', + property: 'x-transition:enterEnd', + boolean: false, + spaceSeparated: true, + commaOrSpaceSeparated: true, +}) +definePropertyInformation('x-transition:leavestart', { + attribute: 'x-transition:leave-start', + property: 'x-transition:leaveStart', + boolean: false, + spaceSeparated: true, + commaOrSpaceSeparated: true, +}) +definePropertyInformation('x-transition:leaveend', { + attribute: 'x-transition:leave-end', + property: 'x-transition:leaveEnd', + boolean: false, + spaceSeparated: true, + commaOrSpaceSeparated: true, +}) + +/** + * Alpine namespaces we handle with special + * rules when stringifying attributes + */ +const alpineNamespaces: Record = { + x: 'x-', + xOn: 'x-on:', + xBind: 'x-bind:', + xTransition: 'x-transition:', +} + +/** + * Raise an `E_UNALLOWED_EXPRESSION` exception. Filename and expression is + * required to point the error stack to the correct file + */ +export function unallowedExpression( + message: string, + filename: string, + loc: { line: number; col: number } +) { + throw new EdgeError(message, 'E_UNALLOWED_EXPRESSION', { + line: loc.line, + col: loc.col, + filename: filename, + }) +} + +/** + * Validates the expression type to be part of the allowed + * expressions only. + * + * The filename is required to report errors. + * + * ```js + * isNotSubsetOf(expression, ['Literal', 'Identifier'], () => {}) + * ``` + */ +export function isSubsetOf( + expression: any, + expressions: ExpressionList, + errorCallback: () => void +) { + if (!expressions.includes(expression.type)) { + errorCallback() + } +} + +/** + * Validates the expression type not to be part of the disallowed + * expressions. + * + * The filename is required to report errors. + * + * ```js + * isNotSubsetOf(expression, 'SequenceExpression', () => {}) + * ``` + */ +export function isNotSubsetOf( + expression: any, + expressions: ExpressionList, + errorCallback: () => void +) { + if (expressions.includes(expression.type)) { + errorCallback() + } +} + +/** + * Parses the jsArg by generating and transforming its AST + */ +export function parseJsArg(parser: Parser, token: TagToken) { + return parser.utils.transformAst( + parser.utils.generateAST(token.properties.jsArg, token.loc, token.filename), + token.filename, + parser + ) +} + +/** + * Each loop. A soft replacement for `lodash.each` that we were using earlier + */ +export function each(collection: any, iteratee: (value: any, key: any) => void) { + if (Array.isArray(collection)) { + for (let [key, value] of collection.entries()) { + iteratee(value, key) + } + return + } + + if (typeof collection === 'string') { + let index = 0 + for (let value of collection) { + iteratee(value, index++) + } + return + } + + if (collection && typeof collection === 'object') { + for (let [key, value] of Object.entries(collection)) { + iteratee(value, key) + } + } +} + +/** + * Async each loop. A soft replacement for `lodash.each` that we were + * using earlier with support for async await + */ +export async function asyncEach( + collection: any, + iteratee: (value: any, key: any) => Promise +) { + if (Array.isArray(collection)) { + for (let [key, value] of collection.entries()) { + await iteratee(value, key) + } + return + } + + if (typeof collection === 'string') { + let index = 0 + for (let value of collection) { + await iteratee(value, index++) + } + return + } + + if (collection && typeof collection === 'object') { + for (let [key, value] of Object.entries(collection)) { + await iteratee(value, key) + } + } +} + +/** + * This class generates a valid object as a string, which is written to the template + * output. The reason we need a string like object, since we don't want it's + * properties to be evaluated during the object creation, instead it must + * be evaluated when the compiled output is invoked. + */ +export class StringifiedObject { + #obj: string = '' + + addSpread(key: string) { + this.#obj += this.#obj.length ? `, ${key}` : `${key}` + } + + /** + * Add key/value pair to the object. + * + * ```js + * stringifiedObject.add('username', `'virk'`) + * ``` + */ + add(key: any, value: any, isComputed: boolean = false) { + key = isComputed ? `[${key}]` : key + this.#obj += this.#obj.length ? `, ${key}: ${value}` : `${key}: ${value}` + } + + /** + * Returns the object alike string back. + * + * ```js + * stringifiedObject.flush() + * + * // returns + * `{ username: 'virk' }` + * ``` + */ + flush(): string { + const obj = `{ ${this.#obj} }` + this.#obj = '' + return obj + } +} + +/** + * Stringify an object to props to HTML attributes + */ +export function stringifyAttributes(props: any, namespace?: string): string { + const attributes = Object.keys(props) + if (attributes.length === 0) { + return '' + } + + return attributes + .reduce((result, key) => { + let value = props[key] + key = namespace ? `${namespace}${key}` : key + + /** + * No value defined, remove attribute + */ + if (!value) { + return result + } + + /** + * Handle alpine properties separately + */ + if (alpineNamespaces[key] && typeof value === 'object') { + result = result.concat(stringifyAttributes(value, alpineNamespaces[key])) + return result + } + + const propInfo = find(html, key) + + /** + * Ignore unknown attributes + */ + if (!propInfo) { + return result + } + + const attribute = propInfo.attribute + + /** + * Boolean properties + */ + if (value === true) { + result.push(attribute) + return result + } + + /** + * Encoding rules for certain properties. + * + * - Class values can be objects with conditionals + * - Non-booleanish and numeric properties will be html escaped + * - Arrays will be concatenated into a string list and html escaped + */ + if (key === 'class') { + value = `"${classNames(value)}"` + } else if (Array.isArray(value)) { + value = `"${value.join(propInfo.commaSeparated ? ',' : ' ')}"` + } else { + value = `"${String(value)}"` + } + + /** + * Push attribute value string + */ + result.push(`${attribute}=${value}`) + return result + }, []) + .join(' ') +} diff --git a/src/utils/index.ts b/src/utils/index.ts deleted file mode 100644 index 86e2fb0..0000000 --- a/src/utils/index.ts +++ /dev/null @@ -1,137 +0,0 @@ -/* - * edge - * - * (c) Harminder Virk - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -import { TagToken } from 'edge-lexer' -import { EdgeError } from 'edge-error' -import { expressions as expressionsList, Parser } from 'edge-parser' - -type ExpressionList = readonly (keyof typeof expressionsList)[] - -/** - * Raise an `E_UNALLOWED_EXPRESSION` exception. Filename and expression is - * required to point the error stack to the correct file - */ -export function unallowedExpression( - message: string, - filename: string, - loc: { line: number; col: number } -) { - throw new EdgeError(message, 'E_UNALLOWED_EXPRESSION', { - line: loc.line, - col: loc.col, - filename: filename, - }) -} - -/** - * Validates the expression type to be part of the allowed - * expressions only. - * - * The filename is required to report errors. - * - * ```js - * isNotSubsetOf(expression, ['Literal', 'Identifier'], () => {}) - * ``` - */ -export function isSubsetOf( - expression: any, - expressions: ExpressionList, - errorCallback: () => void -) { - if (!expressions.includes(expression.type)) { - errorCallback() - } -} - -/** - * Validates the expression type not to be part of the disallowed - * expressions. - * - * The filename is required to report errors. - * - * ```js - * isNotSubsetOf(expression, 'SequenceExpression', () => {}) - * ``` - */ -export function isNotSubsetOf( - expression: any, - expressions: ExpressionList, - errorCallback: () => void -) { - if (expressions.includes(expression.type)) { - errorCallback() - } -} - -/** - * Parses the jsArg by generating and transforming its AST - */ -export function parseJsArg(parser: Parser, token: TagToken) { - return parser.utils.transformAst( - parser.utils.generateAST(token.properties.jsArg, token.loc, token.filename), - token.filename, - parser - ) -} - -/** - * Each loop. A soft replacement for `lodash.each` that we were using earlier - */ -export function each(collection: any, iteratee: (value: any, key: any) => void) { - if (Array.isArray(collection)) { - for (let [key, value] of collection.entries()) { - iteratee(value, key) - } - return - } - - if (typeof collection === 'string') { - let index = 0 - for (let value of collection) { - iteratee(value, index++) - } - return - } - - if (collection && typeof collection === 'object') { - for (let [key, value] of Object.entries(collection)) { - iteratee(value, key) - } - } -} - -/** - * Async each loop. A soft replacement for `lodash.each` that we were - * using earlier with support for async await - */ -export async function asyncEach( - collection: any, - iteratee: (value: any, key: any) => Promise -) { - if (Array.isArray(collection)) { - for (let [key, value] of collection.entries()) { - await iteratee(value, key) - } - return - } - - if (typeof collection === 'string') { - let index = 0 - for (let value of collection) { - await iteratee(value, index++) - } - return - } - - if (collection && typeof collection === 'object') { - for (let [key, value] of Object.entries(collection)) { - await iteratee(value, key) - } - } -} diff --git a/test/assert-extend.ts b/test/assert-extend.ts deleted file mode 100644 index a925732..0000000 --- a/test/assert-extend.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Assert } from '@japa/assert' -import { Assertion } from 'chai' - -declare module '@japa/assert' { - interface Assert { - stringEqual(actual: string, expected: string) - } -} - -Assert.macro('stringEqual', function (actual: string, expected: string) { - this.assertions.total++ - return new Assertion(actual.split(/\r\n|\n/)).to.deep.equal(expected.split(/\r\n|\n/)) -}) diff --git a/test/fixtures.spec.ts b/test/fixtures.spec.ts deleted file mode 100644 index 6ecec6d..0000000 --- a/test/fixtures.spec.ts +++ /dev/null @@ -1,125 +0,0 @@ -/* - * edge-parser - * - * (c) Harminder Virk - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -import './assert-extend' - -import { test } from '@japa/runner' -import { join } from 'path' -import { readdirSync, readFileSync, statSync } from 'fs' - -import * as tags from '../src/Tags' -import { Loader } from '../src/Loader' -import { Template } from '../src/Template' -import { Compiler } from '../src/Compiler' -import { Processor } from '../src/Processor' - -import { normalizeNewLines, normalizeFilename } from '../test-helpers' - -const basePath = join(__dirname, '../fixtures') - -const loader = new Loader() -loader.mount('default', basePath) - -const processor = new Processor() - -test.group('Fixtures', (group) => { - group.setup(() => { - Object.keys(tags).forEach((tag) => { - if (tags[tag].boot) { - tags[tag].boot(Template) - } - }) - }) - - const dirs = readdirSync(basePath).filter((file) => statSync(join(basePath, file)).isDirectory()) - const compiler = new Compiler(loader, tags, processor, { async: false }) - - dirs.forEach((dir) => { - const dirBasePath = join(basePath, dir) - test(dir, ({ assert }) => { - const template = new Template(compiler, {}, {}, processor) - - /** - * Compiled output - */ - const { template: compiled } = compiler.compile(`${dir}/index.edge`) - const expectedCompiled = normalizeNewLines( - readFileSync(join(dirBasePath, 'compiled.js'), 'utf-8') - ) - assert.stringEqual( - compiled, - expectedCompiled - .split('\n') - .map((line) => normalizeFilename(dirBasePath, line)) - .join('\n') - ) - - /** - * Render output - */ - const out = readFileSync(join(dirBasePath, 'index.txt'), 'utf-8') - const state = JSON.parse(readFileSync(join(dirBasePath, 'index.json'), 'utf-8')) - const output = template.render(`${dir}/index.edge`, state) as string - const outputRaw = template.renderRaw( - readFileSync(join(dirBasePath, 'index.edge'), 'utf-8'), - state - ) - assert.stringEqual(output.trim(), out) - assert.stringEqual(outputRaw.trim(), out) - }) - }) -}) - -test.group('Fixtures | Cache', (group) => { - group.setup(() => { - Object.keys(tags).forEach((tag) => { - if (tags[tag].boot) { - tags[tag].boot(Template) - } - }) - }) - - const dirs = readdirSync(basePath).filter((file) => statSync(join(basePath, file)).isDirectory()) - const compiler = new Compiler(loader, tags, processor, { async: false, cache: true }) - - dirs.forEach((dir) => { - const dirBasePath = join(basePath, dir) - test(dir, ({ assert }) => { - const template = new Template(compiler, {}, {}, processor) - - /** - * Compiled output - */ - const { template: compiled } = compiler.compile(`${dir}/index.edge`) - const expectedCompiled = normalizeNewLines( - readFileSync(join(dirBasePath, 'compiled.js'), 'utf-8') - ) - assert.stringEqual( - compiled, - expectedCompiled - .split('\n') - .map((line) => normalizeFilename(dirBasePath, line)) - .join('\n') - ) - - /** - * Render output - */ - const out = readFileSync(join(dirBasePath, 'index.txt'), 'utf-8') - const state = JSON.parse(readFileSync(join(dirBasePath, 'index.json'), 'utf-8')) - const output = template.render(`${dir}/index.edge`, state) as string - const outputRaw = template.renderRaw( - readFileSync(join(dirBasePath, 'index.edge'), 'utf-8'), - state - ) - assert.stringEqual(output.trim(), out) - assert.stringEqual(outputRaw.trim(), out) - }) - }) -}) diff --git a/test/loader.spec.ts b/test/loader.spec.ts deleted file mode 100644 index e6fd4e6..0000000 --- a/test/loader.spec.ts +++ /dev/null @@ -1,163 +0,0 @@ -/* - * edge - * - * (c) Harminder Virk - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -import { test } from '@japa/runner' -import { join } from 'path' -import { Filesystem } from '@poppinss/dev-utils' - -import { Loader } from '../src/Loader' - -const fs = new Filesystem(join(__dirname, 'views')) - -test.group('Loader', (group) => { - group.each.teardown(async () => { - await fs.cleanup() - }) - - test('mount path with a name', ({ assert }) => { - const loader = new Loader() - loader.mount('default', fs.basePath) - assert.deepEqual(loader.mounted, { default: fs.basePath }) - }) - - test('unmount path with a name', ({ assert }) => { - const loader = new Loader() - loader.mount('default', fs.basePath) - loader.unmount('default') - assert.deepEqual(loader.mounted, {}) - }) - - test('throw exception when resolving path from undefined location', ({ assert }) => { - const loader = new Loader() - const fn = () => loader.resolve('foo') - assert.throws(fn, '"default" namespace is not mounted') - }) - - test('resolve template for the default disk', async ({ assert }) => { - await fs.add('foo.edge', 'Hello world') - - const loader = new Loader() - loader.mount('default', fs.basePath) - - const { template } = loader.resolve('foo') - assert.equal(template.trim(), 'Hello world') - }) - - test('raise error when template is missing', async ({ assert }) => { - const loader = new Loader() - loader.mount('default', fs.basePath) - - const fn = () => loader.resolve('foo') - assert.throws( - fn, - `Cannot resolve "${join(fs.basePath, 'foo.edge')}". Make sure the file exists` - ) - }) - - test('resolve template with extension', async ({ assert }) => { - await fs.add('foo.edge', 'Hello world') - - const loader = new Loader() - loader.mount('default', fs.basePath) - - const { template } = loader.resolve('foo.edge') - assert.equal(template.trim(), 'Hello world') - }) - - test('resolve template from a named disk', async ({ assert }) => { - await fs.add('foo.edge', 'Hello world') - - const loader = new Loader() - loader.mount('users', fs.basePath) - - const { template } = loader.resolve('users::foo.edge') - assert.equal(template.trim(), 'Hello world') - }) - - test('do not replace edge within the template path name', async ({ assert }) => { - const loader = new Loader() - loader.mount('default', fs.basePath) - - const templatePath = loader.makePath('edge-partial.edge') - assert.equal(templatePath, join(fs.basePath, 'edge-partial.edge')) - }) - - test('do not replace edge within the template path seperator', async ({ assert }) => { - const loader = new Loader() - loader.mount('default', fs.basePath) - - const templatePath = loader.makePath('partial/edge') - assert.equal(templatePath, join(fs.basePath, 'partial/edge.edge')) - }) - - test('do not replace edge within the template path seperator with extension', async ({ - assert, - }) => { - const loader = new Loader() - loader.mount('default', fs.basePath) - - const templatePath = loader.makePath('partial/edge.edge') - assert.equal(templatePath, join(fs.basePath, 'partial/edge.edge')) - }) - - test('do not replace edge within the template path seperator with named disk', async ({ - assert, - }) => { - const loader = new Loader() - loader.mount('users', fs.basePath) - - const templatePath = loader.makePath('users::partial/edge.edge') - assert.equal(templatePath, join(fs.basePath, 'partial/edge.edge')) - }) - - test('pre register templates with a key', async ({ assert }) => { - const loader = new Loader() - loader.register('my-view', { - template: 'Hello world', - }) - - const { template } = loader.resolve('my-view') - assert.equal(template.trim(), 'Hello world') - }) - - test('remove registered template', async ({ assert }) => { - const loader = new Loader() - loader.register('my-view', { - template: 'Hello world', - }) - loader.mount('default', __dirname) - - const { template } = loader.resolve('my-view') - assert.equal(template.trim(), 'Hello world') - - loader.remove('my-view') - assert.throws( - () => loader.resolve('my-view'), - `Cannot resolve "${join(__dirname, 'my-view.edge')}". Make sure the file exists` - ) - }) - - test('pre registering duplicate templates must raise an error', async ({ assert }) => { - const loader = new Loader() - loader.register('my-view', { template: 'Hello world' }) - const fn = () => loader.register('my-view', { template: 'Hello world' }) - - assert.throws(fn, 'Cannot override previously registered "my-view" template') - }) - - test('resolve template with dot seperator', async ({ assert }) => { - await fs.add('foo/bar.edge', 'Hello world') - - const loader = new Loader() - loader.mount('default', fs.basePath) - - const { template } = loader.resolve('foo.bar') - assert.equal(template.trim(), 'Hello world') - }) -}) diff --git a/test/stringified-object.spec.ts b/test/stringified-object.spec.ts deleted file mode 100644 index a5c2ca4..0000000 --- a/test/stringified-object.spec.ts +++ /dev/null @@ -1,173 +0,0 @@ -/* - * edge - * - * (c) Harminder Virk - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -import { test } from '@japa/runner' -import { Parser, Stack } from 'edge-parser' -import { StringifiedObject } from '../src/StringifiedObject' - -/** - * Sample loc - */ -const LOC = { - start: { - line: 1, - col: 0, - }, - end: { - line: 1, - col: 0, - }, -} - -test.group('StringifiedObject', () => { - test('add string as a key-value pair to object', ({ assert }) => { - const stringified = new StringifiedObject() - stringified.add('username', "'virk'") - assert.equal(stringified.flush(), "{ username: 'virk' }") - }) - - test('add number as a key-value pair to object', ({ assert }) => { - const stringified = new StringifiedObject() - stringified.add('username', '22') - assert.equal(stringified.flush(), '{ username: 22 }') - }) - - test('add boolean as a key-value pair to object', ({ assert }) => { - const stringified = new StringifiedObject() - stringified.add('username', 'true') - assert.equal(stringified.flush(), '{ username: true }') - }) - - test('add object as a key-value pair to object', ({ assert }) => { - const stringified = new StringifiedObject() - stringified.add('username', '{ age: 22 }') - assert.equal(stringified.flush(), '{ username: { age: 22 } }') - }) - - test('add array as a key-value pair to object', ({ assert }) => { - const stringified = new StringifiedObject() - stringified.add('username', '[10, 20]') - assert.equal(stringified.flush(), '{ username: [10, 20] }') - }) -}) - -test.group('StringifiedObject | fromAcornAst', () => { - test('stringify object expression', ({ assert }) => { - const parser = new Parser({}, new Stack(), { - async: false, - statePropertyName: 'state', - escapeCallPath: 'escape', - }) - const expression = parser.utils.transformAst( - parser.utils.generateAST("({ username: 'virk' })", LOC, 'eval.edge'), - 'eval.edge', - parser - ) - - const props = StringifiedObject.fromAcornExpressions([expression], parser) - assert.equal(props, "{ username: 'virk' }") - }) - - test('parse props with shorthand obj', ({ assert }) => { - const parser = new Parser({}, new Stack(), { - async: false, - statePropertyName: 'state', - escapeCallPath: 'escape', - }) - const expression = parser.utils.transformAst( - parser.utils.generateAST('({ username })', LOC, 'eval.edge'), - 'eval.edge', - parser - ) - - const props = StringifiedObject.fromAcornExpressions([expression], parser) - assert.equal(props, '{ username: state.username }') - }) - - test('parse props with computed obj', ({ assert }) => { - const parser = new Parser({}, new Stack(), { - async: false, - statePropertyName: 'state', - escapeCallPath: 'escape', - }) - const expression = parser.utils.transformAst( - parser.utils.generateAST('({ [username]: username })', LOC, 'eval.edge'), - 'eval.edge', - parser - ) - - const props = StringifiedObject.fromAcornExpressions([expression], parser) - assert.equal(props, '{ [state.username]: state.username }') - }) - - test('parse props with multiple obj properties', ({ assert }) => { - const parser = new Parser({}, new Stack(), { - async: false, - statePropertyName: 'state', - escapeCallPath: 'escape', - }) - const expression = parser.utils.transformAst( - parser.utils.generateAST("({ username: 'virk', age: 22 })", LOC, 'eval.edge'), - 'eval.edge', - parser - ) - - const props = StringifiedObject.fromAcornExpressions([expression], parser) - - assert.equal(props, "{ username: 'virk', age: 22 }") - }) - - test('parse props with shorthand and full properties', ({ assert }) => { - const parser = new Parser({}, new Stack(), { - async: false, - statePropertyName: 'state', - escapeCallPath: 'escape', - }) - const expression = parser.utils.transformAst( - parser.utils.generateAST('({ username, age: 22 })', LOC, 'eval.edge'), - 'eval.edge', - parser - ) - - const props = StringifiedObject.fromAcornExpressions([expression], parser) - assert.equal(props, '{ username: state.username, age: 22 }') - }) - - test('parse props with assignment expression', ({ assert }) => { - const parser = new Parser({}, new Stack(), { - async: false, - statePropertyName: 'state', - escapeCallPath: 'escape', - }) - const expression = parser.utils.transformAst( - parser.utils.generateAST("(title = 'Hello')", LOC, 'eval.edge'), - 'eval.edge', - parser - ) - - const props = StringifiedObject.fromAcornExpressions([expression], parser) - assert.equal(props, "{ title: 'Hello' }") - }) - - test('parse props with more than one assignment expression', ({ assert }) => { - const parser = new Parser({}, new Stack(), { - async: false, - statePropertyName: 'state', - escapeCallPath: 'escape', - }) - const expression = parser.utils.transformAst( - parser.utils.generateAST("(title = 'Hello', body = 'Some content')", LOC, 'eval.edge'), - 'eval.edge', - parser - ) - - const props = StringifiedObject.fromAcornExpressions(expression.expressions, parser) - assert.equal(props, "{ title: 'Hello', body: 'Some content' }") - }) -}) diff --git a/tests/assert_extend.ts b/tests/assert_extend.ts new file mode 100644 index 0000000..d51040f --- /dev/null +++ b/tests/assert_extend.ts @@ -0,0 +1,15 @@ +import { Assert } from '@japa/assert' + +declare module '@japa/assert' { + interface Assert { + stringEqual(actual: string, expected: string, message?: string): void + } +} + +Assert.macro( + 'stringEqual', + function (this: Assert, actual: string, expected: string, msg?: string) { + this.incrementAssertionsCount() + return new this.Assertion(actual.split(/\r\n|\n/), msg).to.deep.equal(expected.split(/\r\n|\n/)) + } +) diff --git a/test/async-fixtures.spec.ts b/tests/async_fixtures.spec.ts similarity index 53% rename from test/async-fixtures.spec.ts rename to tests/async_fixtures.spec.ts index 254d6c5..6197b83 100644 --- a/test/async-fixtures.spec.ts +++ b/tests/async_fixtures.spec.ts @@ -1,27 +1,30 @@ /* - * edge-parser + * edge.js-parser * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import './assert-extend' +import './assert_extend.js' +import dedent from 'dedent-js' import { test } from '@japa/runner' -import { join } from 'path' -import { readdirSync, readFileSync, statSync } from 'fs' +import { fileURLToPath } from 'node:url' +import { dirname, join } from 'node:path' +import { readdirSync, readFileSync, statSync } from 'node:fs' -import * as tags from '../src/Tags' -import { Loader } from '../src/Loader' -import { Template } from '../src/Template' -import { Compiler } from '../src/Compiler' -import { Processor } from '../src/Processor' +import * as tags from '../src/tags/main.js' +import { Loader } from '../src/loader.js' +import { Template } from '../src/template.js' +import { Compiler } from '../src/compiler.js' +import { Processor } from '../src/processor.js' +import * as compatTags from '../src/migrate/tags/main.js' -import { normalizeNewLines, normalizeFilename } from '../test-helpers' +import { normalizeNewLines, normalizeFilename } from '../tests_helpers/index.js' -const basePath = join(__dirname, '../async-fixtures') +const basePath = join(dirname(fileURLToPath(import.meta.url)), '../async_fixtures') const loader = new Loader() loader.mount('default', basePath) @@ -30,34 +33,49 @@ const processor = new Processor() test.group('Async Fixtures', (group) => { group.setup(() => { Object.keys(tags).forEach((tag) => { - if (tags[tag].boot) { - tags[tag].boot(Template) - } + tags[tag as keyof typeof tags].boot?.(Template) + }) + Object.keys(compatTags).forEach((tag) => { + compatTags[tag as keyof typeof compatTags].boot?.(Template) }) }) const dirs = readdirSync(basePath).filter((file) => statSync(join(basePath, file)).isDirectory()) - const compiler = new Compiler(loader, tags, processor, { async: true }) + const compiler = new Compiler( + loader, + { + ...tags, + ...compatTags, + }, + processor, + { async: true } + ) dirs.forEach((dir) => { const dirBasePath = join(basePath, dir) + const compatMode = dir.endsWith('-compat') + test(dir, async ({ assert }) => { const template = new Template(compiler, {}, {}, processor) + compiler.compat = compatMode /** * Compiled output */ - const { template: compiled } = compiler.compile(`${dir}/index.edge`) + const compiledTemplate = compiler.compile(`${dir}/index.edge`) const expectedCompiled = normalizeNewLines( readFileSync(join(dirBasePath, 'compiled.js'), 'utf-8') ) assert.stringEqual( - compiled, - expectedCompiled - .split('\n') - .map((line) => normalizeFilename(dirBasePath, line)) - .join('\n') + compiledTemplate.toString(), + dedent`async function anonymous(template,state,$context + ) { + ${expectedCompiled + .split('\n') + .map((line) => normalizeFilename(dirBasePath, line)) + .join('\n')} + }` ) /** @@ -72,41 +90,56 @@ test.group('Async Fixtures', (group) => { ) assert.stringEqual(output.trim(), out) assert.stringEqual(outputRaw.trim(), out) - }) + }).tags(compatMode ? ['compat'] : []) }) }) test.group('Async Fixtures | Cached', (group) => { group.setup(() => { Object.keys(tags).forEach((tag) => { - if (tags[tag].boot) { - tags[tag].boot(Template) - } + tags[tag as keyof typeof tags].boot?.(Template) + }) + Object.keys(compatTags).forEach((tag) => { + compatTags[tag as keyof typeof compatTags].boot?.(Template) }) }) const dirs = readdirSync(basePath).filter((file) => statSync(join(basePath, file)).isDirectory()) - const compiler = new Compiler(loader, tags, processor, { async: true, cache: true }) + const compiler = new Compiler( + loader, + { + ...tags, + ...compatTags, + }, + processor, + { async: true, cache: true } + ) dirs.forEach((dir) => { const dirBasePath = join(basePath, dir) + const compatMode = dir.endsWith('-compat') + test(dir, async ({ assert }) => { const template = new Template(compiler, {}, {}, processor) + compiler.compat = compatMode /** * Compiled output */ - const { template: compiled } = compiler.compile(`${dir}/index.edge`) + const compiledTemplate = compiler.compile(`${dir}/index.edge`) const expectedCompiled = normalizeNewLines( readFileSync(join(dirBasePath, 'compiled.js'), 'utf-8') ) assert.stringEqual( - compiled, - expectedCompiled - .split('\n') - .map((line) => normalizeFilename(dirBasePath, line)) - .join('\n') + compiledTemplate.toString(), + dedent`async function anonymous(template,state,$context + ) { + ${expectedCompiled + .split('\n') + .map((line) => normalizeFilename(dirBasePath, line)) + .join('\n')} + }` ) /** @@ -121,6 +154,6 @@ test.group('Async Fixtures | Cached', (group) => { ) assert.stringEqual(output.trim(), out) assert.stringEqual(outputRaw.trim(), out) - }) + }).tags(compatMode ? ['compat'] : []) }) }) diff --git a/test/compiler.spec.ts b/tests/compiler.spec.ts similarity index 81% rename from test/compiler.spec.ts rename to tests/compiler.spec.ts index 91833a3..17bf98d 100644 --- a/test/compiler.spec.ts +++ b/tests/compiler.spec.ts @@ -1,50 +1,46 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { test } from '@japa/runner' -import { join } from 'path' +import './assert_extend.js' import dedent from 'dedent-js' +import { join } from 'node:path' +import { test } from '@japa/runner' +// @ts-ignore untyped module import stringify from 'js-stringify' -import { Filesystem } from '@poppinss/dev-utils' import { TagTypes, MustacheTypes } from 'edge-lexer' -import { Loader } from '../src/Loader' -import { setTag } from '../src/Tags/Set' -import { Template } from '../src/Template' -import { Compiler } from '../src/Compiler' -import { Processor } from '../src/Processor' -import { layoutTag } from '../src/Tags/Layout' -import { sectionTag } from '../src/Tags/Section' -import { componentTag } from '../src/Tags/Component' -import { normalizeNewLines } from '../test-helpers' - -import './assert-extend' +import { Loader } from '../src/loader.js' +import { Template } from '../src/template.js' +import { Compiler } from '../src/compiler.js' +import { Processor } from '../src/processor.js' +import { componentTag } from '../src/tags/component.js' +import { normalizeNewLines } from '../tests_helpers/index.js' -const fs = new Filesystem(join(__dirname, 'views')) - -test.group('Compiler | Cache', (group) => { - group.each.teardown(async () => { - await fs.cleanup() - }) +import { setTag } from '../src/migrate/tags/set.js' +import { sectionTag } from '../src/migrate/tags/section.js' +import { layoutTag } from '../src/migrate/tags/layout.js' - test('compile template', async ({ assert }) => { - await fs.add('foo.edge', 'Hello {{ username }}') +test.group('Compiler | Cache', () => { + test('compile template', async ({ assert, fs }) => { + await fs.create('foo.edge', 'Hello {{ username }}') const loader = new Loader() loader.mount('default', fs.basePath) const compiler = new Compiler(loader, {}, new Processor()) - const { template } = compiler.compile('foo') + const compiledTemplate = compiler.compile('foo') assert.stringEqual( - template, - normalizeNewLines(dedent`let out = ""; + compiledTemplate.toString(), + normalizeNewLines(dedent`function anonymous(template,state,$context + ) { + let out = ""; let $lineNumber = 1; let $filename = ${stringify(join(fs.basePath, 'foo.edge'))}; try { @@ -53,25 +49,26 @@ test.group('Compiler | Cache', (group) => { } catch (error) { template.reThrow(error, $filename, $lineNumber); } - return out;`) + return out; + }`) ) }) - test('save template to cache when caching is turned on', async ({ assert }) => { - await fs.add('foo.edge', 'Hello {{ username }}') + test('save template to cache when caching is turned on', async ({ assert, fs }) => { + await fs.create('foo.edge', 'Hello {{ username }}') const loader = new Loader() loader.mount('default', fs.basePath) const compiler = new Compiler(loader, {}, new Processor(), { cache: true }) - assert.equal( - compiler.compile('foo').template, - compiler.cacheManager.get(join(fs.basePath, 'foo.edge'))!.template + assert.strictEqual( + compiler.compile('foo'), + compiler.cacheManager.get(join(fs.basePath, 'foo.edge')) ) }) - test('do not cache template when caching is turned off', async ({ assert }) => { - await fs.add('foo.edge', 'Hello {{ username }}') + test('do not cache template when caching is turned off', async ({ assert, fs }) => { + await fs.create('foo.edge', 'Hello {{ username }}') const loader = new Loader() loader.mount('default', fs.basePath) @@ -82,13 +79,11 @@ test.group('Compiler | Cache', (group) => { }) }) -test.group('Compiler | Tokenize', (group) => { - group.each.teardown(async () => { - await fs.cleanup() - }) +test.group('Compiler | Tokenize | compat', (group) => { + group.tap((t) => t.tags(['compat'])) - test('during tokenize, merge @section tags of a given layout', async ({ assert }) => { - await fs.add( + test('during tokenize, merge @section tags of a given layout', async ({ assert, fs }) => { + await fs.create( 'master.edge', dedent` Master page @@ -96,7 +91,7 @@ test.group('Compiler | Tokenize', (group) => { ` ) - await fs.add( + await fs.create( 'index.edge', dedent` @layout('master') @@ -115,6 +110,7 @@ test.group('Compiler | Tokenize', (group) => { } const compiler = new Compiler(loader, tags, new Processor()) + compiler.compat = true assert.deepEqual(compiler.tokenize('index.edge'), [ { @@ -148,8 +144,8 @@ test.group('Compiler | Tokenize', (group) => { ]) }) - test('during tokenize, merge @set tags of a given layout', async ({ assert }) => { - await fs.add( + test('during tokenize merge set tags of a given layout', async ({ assert, fs }) => { + await fs.create( 'master.edge', dedent` Master page @@ -157,7 +153,7 @@ test.group('Compiler | Tokenize', (group) => { ` ) - await fs.add( + await fs.create( 'index.edge', dedent` @layout('master') @@ -178,6 +174,7 @@ test.group('Compiler | Tokenize', (group) => { } const compiler = new Compiler(loader, tags, new Processor()) + compiler.compat = true assert.deepEqual(compiler.tokenize('index.edge'), [ { @@ -221,10 +218,13 @@ test.group('Compiler | Tokenize', (group) => { ]) }) - test('ensure template extending layout can only use section or set tags', async ({ assert }) => { + test('ensure template extending layout can only use section or set tags', async ({ + assert, + fs, + }) => { assert.plan(4) - await fs.add( + await fs.create( 'master.edge', dedent` Master page @@ -232,7 +232,7 @@ test.group('Compiler | Tokenize', (group) => { ` ) - await fs.add( + await fs.create( 'index.edge', dedent` @layout('master') @@ -248,6 +248,7 @@ test.group('Compiler | Tokenize', (group) => { } const compiler = new Compiler(loader, tags, new Processor()) + compiler.compat = true try { compiler.tokenize('index.edge') @@ -262,8 +263,8 @@ test.group('Compiler | Tokenize', (group) => { } }) - test('during tokenize, merge @section tags of a nested layouts', async ({ assert }) => { - await fs.add( + test('during tokenize merge @section tags of a nested layouts', async ({ assert, fs }) => { + await fs.create( 'super-master.edge', dedent` Master page @@ -272,7 +273,7 @@ test.group('Compiler | Tokenize', (group) => { ` ) - await fs.add( + await fs.create( 'master.edge', dedent` @layout('super-master') @@ -283,7 +284,7 @@ test.group('Compiler | Tokenize', (group) => { ` ) - await fs.add( + await fs.create( 'index.edge', dedent` @layout('master') @@ -301,6 +302,7 @@ test.group('Compiler | Tokenize', (group) => { } const compiler = new Compiler(loader, tags, new Processor()) + compiler.compat = true assert.deepEqual(compiler.tokenize('index.edge'), [ { @@ -361,8 +363,8 @@ test.group('Compiler | Tokenize', (group) => { ]) }) - test('layout tokens must point to its own filename', async ({ assert }) => { - await fs.add( + test('layout tokens must point to its own filename', async ({ assert, fs }) => { + await fs.create( 'master.edge', dedent` {{ username }} @@ -370,7 +372,7 @@ test.group('Compiler | Tokenize', (group) => { ` ) - await fs.add( + await fs.create( 'index.edge', dedent` @layout('master') @@ -389,6 +391,7 @@ test.group('Compiler | Tokenize', (group) => { } const compiler = new Compiler(loader, tags, new Processor()) + compiler.compat = true assert.deepEqual(compiler.tokenize('index.edge'), [ { @@ -426,13 +429,11 @@ test.group('Compiler | Tokenize', (group) => { }) }) -test.group('Compiler | Compile', (group) => { - group.each.teardown(async () => { - await fs.cleanup() - }) +test.group('Compiler | Compile | compat', (group) => { + group.tap((t) => t.tags(['compat'])) - test('compile template with layouts', async ({ assert }) => { - await fs.add( + test('compile template with layouts', async ({ assert, fs }) => { + await fs.create( 'master.edge', dedent` {{ username }} @@ -440,7 +441,7 @@ test.group('Compiler | Compile', (group) => { ` ) - await fs.add( + await fs.create( 'index.edge', dedent` @layout('master') @@ -458,10 +459,13 @@ test.group('Compiler | Compile', (group) => { } const compiler = new Compiler(loader, tags, new Processor()) + compiler.compat = true assert.stringEqual( - compiler.compile('index.edge').template, - normalizeNewLines(dedent`let out = ""; + compiler.compile('index.edge').toString(), + normalizeNewLines(dedent`function anonymous(template,state,$context + ) { + let out = ""; let $lineNumber = 1; let $filename = ${stringify(join(fs.basePath, 'index.edge'))}; try { @@ -476,14 +480,14 @@ test.group('Compiler | Compile', (group) => { template.reThrow(error, $filename, $lineNumber); } return out; - `) + }`) ) }) - test('compile errors inside layout must point to the right file', async ({ assert }) => { + test('compile errors inside layout must point to the right file', async ({ assert, fs }) => { assert.plan(3) - await fs.add( + await fs.create( 'master.edge', dedent` {{ user name }} @@ -491,7 +495,7 @@ test.group('Compiler | Compile', (group) => { ` ) - await fs.add( + await fs.create( 'index.edge', dedent` @layout('master') @@ -512,6 +516,7 @@ test.group('Compiler | Compile', (group) => { }, new Processor() ) + compiler.compat = true try { compiler.compile('index.edge') @@ -522,10 +527,10 @@ test.group('Compiler | Compile', (group) => { } }) - test('compile errors parent template must point to the right file', async ({ assert }) => { + test('compile errors parent template must point to the right file', async ({ assert, fs }) => { assert.plan(3) - await fs.add( + await fs.create( 'master.edge', dedent` {{ username }} @@ -533,7 +538,7 @@ test.group('Compiler | Compile', (group) => { ` ) - await fs.add( + await fs.create( 'index.edge', dedent` @layout('master') @@ -554,6 +559,7 @@ test.group('Compiler | Compile', (group) => { }, new Processor() ) + compiler.compat = true try { compiler.compile('index.edge') @@ -564,10 +570,10 @@ test.group('Compiler | Compile', (group) => { } }) - test('runtime errors inside layout must point to the right file', async ({ assert }) => { + test('runtime errors inside layout must point to the right file', async ({ assert, fs }) => { assert.plan(4) - await fs.add( + await fs.create( 'master.edge', dedent` {{ getUserName() }} @@ -575,7 +581,7 @@ test.group('Compiler | Compile', (group) => { ` ) - await fs.add( + await fs.create( 'index.edge', dedent` @layout('master') @@ -596,10 +602,11 @@ test.group('Compiler | Compile', (group) => { }, new Processor() ) + compiler.compat = true try { - const fn = compiler.compile('index.edge').template - new Function('template', 'state', fn)(new Template(compiler, {}, {}, new Processor()), {}) + const fn = compiler.compile('index.edge') + fn(new Template(compiler, {}, {}, new Processor()), {}, {}) } catch (error) { assert.equal(error.message, 'getUserName is not a function') assert.equal(error.filename, join(fs.basePath, 'master.edge')) @@ -608,10 +615,13 @@ test.group('Compiler | Compile', (group) => { } }) - test('runtime errors inside parent template must point to the right file', async ({ assert }) => { + test('runtime errors inside parent template must point to the right file', async ({ + assert, + fs, + }) => { assert.plan(4) - await fs.add( + await fs.create( 'master.edge', dedent` {{ username }} @@ -619,7 +629,7 @@ test.group('Compiler | Compile', (group) => { ` ) - await fs.add( + await fs.create( 'index.edge', dedent` @layout('master') @@ -640,10 +650,11 @@ test.group('Compiler | Compile', (group) => { }, new Processor() ) + compiler.compat = true try { - const fn = compiler.compile('index.edge').template - new Function('template', 'state', fn)(new Template(compiler, {}, {}, new Processor()), {}) + const fn = compiler.compile('index.edge') + fn(new Template(compiler, {}, {}, new Processor()), {}, {}) } catch (error) { assert.equal(error.message, 'getContent is not a function') assert.equal(error.filename, join(fs.basePath, 'index.edge')) @@ -653,13 +664,11 @@ test.group('Compiler | Compile', (group) => { }) }) -test.group('Compiler | Compile Raw', (group) => { - group.each.teardown(async () => { - await fs.cleanup() - }) +test.group('Compiler | Compile Raw | compat', (group) => { + group.tap((t) => t.tags(['compat'])) - test('compile template with layouts', async ({ assert }) => { - await fs.add( + test('compile template with layouts', async ({ assert, fs }) => { + await fs.create( 'master.edge', dedent` {{ username }} @@ -675,15 +684,22 @@ test.group('Compiler | Compile Raw', (group) => { } const compiler = new Compiler(loader, tags, new Processor()) + compiler.compat = true assert.stringEqual( - compiler.compileRaw(dedent` + compiler + .compileRaw( + dedent` @layout('master') @section('content') {{ content }} @endsection - `).template, - normalizeNewLines(dedent`let out = ""; + ` + ) + .toString(), + normalizeNewLines(dedent`function anonymous(template,state,$context + ) { + let out = ""; let $lineNumber = 1; let $filename = ${stringify('eval.edge')}; try { @@ -698,14 +714,14 @@ test.group('Compiler | Compile Raw', (group) => { template.reThrow(error, $filename, $lineNumber); } return out; - `) + }`) ) }) - test('compile errors inside layout must point to the right file', async ({ assert }) => { + test('compile errors inside layout must point to the right file', async ({ assert, fs }) => { assert.plan(3) - await fs.add( + await fs.create( 'master.edge', dedent` {{ user name }} @@ -724,6 +740,7 @@ test.group('Compiler | Compile Raw', (group) => { }, new Processor() ) + compiler.compat = true try { compiler.compileRaw(dedent` @@ -739,10 +756,10 @@ test.group('Compiler | Compile Raw', (group) => { } }) - test('compile errors parent template must point to the right file', async ({ assert }) => { + test('compile errors parent template must point to the right file', async ({ assert, fs }) => { assert.plan(3) - await fs.add( + await fs.create( 'master.edge', dedent` {{ username }} @@ -761,6 +778,7 @@ test.group('Compiler | Compile Raw', (group) => { }, new Processor() ) + compiler.compat = true try { compiler.compileRaw(dedent` @@ -776,10 +794,10 @@ test.group('Compiler | Compile Raw', (group) => { } }) - test('runtime errors inside layout must point to the right file', async ({ assert }) => { + test('runtime errors inside layout must point to the right file', async ({ assert, fs }) => { assert.plan(4) - await fs.add( + await fs.create( 'master.edge', dedent` {{ getUserName() }} @@ -798,15 +816,18 @@ test.group('Compiler | Compile Raw', (group) => { }, new Processor() ) + compiler.compat = true try { - const fn = compiler.compileRaw(dedent` + const fn = compiler.compileRaw( + dedent` @layout('master') @section('content') {{ content }} @endsection - `).template - new Function('template', 'state', fn)(new Template(compiler, {}, {}, new Processor()), {}) + ` + ) + fn(new Template(compiler, {}, {}, new Processor()), {}, {}) } catch (error) { assert.equal(error.message, 'getUserName is not a function') assert.equal(error.filename, join(fs.basePath, 'master.edge')) @@ -815,10 +836,13 @@ test.group('Compiler | Compile Raw', (group) => { } }) - test('runtime errors inside parent template must point to the right file', async ({ assert }) => { + test('runtime errors inside parent template must point to the right file', async ({ + assert, + fs, + }) => { assert.plan(4) - await fs.add( + await fs.create( 'master.edge', dedent` {{ username }} @@ -837,15 +861,18 @@ test.group('Compiler | Compile Raw', (group) => { }, new Processor() ) + compiler.compat = true try { - const fn = compiler.compileRaw(dedent` + const fn = compiler.compileRaw( + dedent` @layout('master') @section('content') {{ getContent() }} @endsection - `).template - new Function('template', 'state', fn)(new Template(compiler, {}, {}, new Processor()), {}) + ` + ) + fn(new Template(compiler, {}, {}, new Processor()), {}, {}) } catch (error) { assert.equal(error.message, 'getContent is not a function') assert.equal(error.filename, 'eval.edge') @@ -855,14 +882,10 @@ test.group('Compiler | Compile Raw', (group) => { }) }) -test.group('Compiler | Processor', (group) => { - group.each.teardown(async () => { - await fs.cleanup() - }) - - test('execute raw processor function', async ({ assert }) => { +test.group('Compiler | Processor', () => { + test('execute raw processor function', async ({ assert, fs }) => { assert.plan(2) - await fs.add('index.edge', dedent`Hello`) + await fs.create('index.edge', dedent`Hello`) const loader = new Loader() loader.mount('default', fs.basePath) @@ -885,9 +908,9 @@ test.group('Compiler | Processor', (group) => { compiler.compile('index') }) - test('use return value of the processor function', async ({ assert }) => { + test('use return value of the processor function', async ({ assert, fs }) => { assert.plan(5) - await fs.add('index.edge', dedent`Hello`) + await fs.create('index.edge', dedent`Hello`) const loader = new Loader() loader.mount('default', fs.basePath) @@ -915,8 +938,10 @@ test.group('Compiler | Processor', (group) => { ) assert.stringEqual( - compiler.compile('index.edge').template, - normalizeNewLines(dedent`let out = ""; + compiler.compile('index.edge').toString(), + normalizeNewLines(dedent`function anonymous(template,state,$context + ) { + let out = ""; let $lineNumber = 1; let $filename = ${stringify(join(fs.basePath, 'index.edge'))}; try { @@ -925,13 +950,13 @@ test.group('Compiler | Processor', (group) => { template.reThrow(error, $filename, $lineNumber); } return out; - `) + }`) ) }) - test('do not run raw processor when template is cached', async ({ assert }) => { + test('do not run raw processor when template is cached', async ({ assert, fs }) => { assert.plan(2) - await fs.add('index.edge', dedent`Hello`) + await fs.create('index.edge', dedent`Hello`) const loader = new Loader() loader.mount('default', fs.basePath) @@ -957,9 +982,9 @@ test.group('Compiler | Processor', (group) => { compiler.compile('index.edge') }) - test('run raw processor function when template is not cached', async ({ assert }) => { + test('run raw processor function when template is not cached', async ({ assert, fs }) => { assert.plan(6) - await fs.add('index.edge', dedent`Hello`) + await fs.create('index.edge', dedent`Hello`) const loader = new Loader() loader.mount('default', fs.basePath) @@ -985,9 +1010,9 @@ test.group('Compiler | Processor', (group) => { compiler.compile('index.edge') }) - test('run compiled processor function', async ({ assert }) => { + test('run compiled processor function', async ({ assert, fs }) => { assert.plan(2) - await fs.add('index.edge', dedent`Hello`) + await fs.create('index.edge', dedent`Hello`) const loader = new Loader() loader.mount('default', fs.basePath) @@ -1022,9 +1047,9 @@ test.group('Compiler | Processor', (group) => { compiler.compile('index.edge') }) - test('use return value of the compiled processor function', async ({ assert }) => { + test('use return value of the compiled processor function', async ({ assert, fs }) => { assert.plan(5) - await fs.add('index.edge', dedent`Hello`) + await fs.create('index.edge', dedent`Hello`) const loader = new Loader() loader.mount('default', fs.basePath) @@ -1063,12 +1088,18 @@ test.group('Compiler | Processor', (group) => { processor ) - assert.equal(compiler.compile('index.edge').template, 'bar') + assert.equal( + compiler.compile('index.edge').toString(), + dedent`function anonymous(template,state,$context + ) { + bar + }` + ) }) - test('run compiled processor function even when template is cached', async ({ assert }) => { - assert.plan(6) - await fs.add('index.edge', dedent`Hello`) + test('do not run compiled function when template is called', async ({ assert, fs }) => { + assert.plan(2) + await fs.create('index.edge', dedent`Hello`) const loader = new Loader() loader.mount('default', fs.basePath) @@ -1106,53 +1137,10 @@ test.group('Compiler | Processor', (group) => { compiler.compile('index.edge') }) - test('do not mutate cache when compiled processor function returns a different value', async ({ - assert, - }) => { - assert.plan(9) - await fs.add('index.edge', dedent`Hello`) - - const loader = new Loader() - loader.mount('default', fs.basePath) - - const processor = new Processor() - processor.process('compiled', ({ compiled, path }) => { - assert.stringEqual( - compiled, - normalizeNewLines(dedent`let out = ""; - let $lineNumber = 1; - let $filename = ${stringify(join(fs.basePath, 'index.edge'))}; - try { - out += "Hello"; - } catch (error) { - template.reThrow(error, $filename, $lineNumber); - } - return out; - `) - ) - assert.equal(path, join(fs.basePath, 'index.edge')) - return 'foo' - }) - - const compiler = new Compiler( - loader, - { - section: sectionTag, - layout: layoutTag, - }, - processor, - { cache: true } - ) - - assert.equal(compiler.compile('index.edge').template, 'foo') - assert.equal(compiler.compile('index.edge').template, 'foo') - assert.equal(compiler.compile('index.edge').template, 'foo') - }) - - test('run raw processor function for layouts', async ({ assert }) => { + test('run raw processor function for layouts', async ({ assert, fs }) => { assert.plan(5) - await fs.add( + await fs.create( 'master.edge', dedent` {{ username }} @@ -1160,7 +1148,7 @@ test.group('Compiler | Processor', (group) => { ` ) - await fs.add( + await fs.create( 'index.edge', dedent` @layout('master') @@ -1211,10 +1199,13 @@ test.group('Compiler | Processor', (group) => { }, processor ) + compiler.compat = true assert.stringEqual( - compiler.compile('index.edge').template, - normalizeNewLines(dedent`let out = ""; + compiler.compile('index.edge').toString(), + normalizeNewLines(dedent`function anonymous(template,state,$context + ) { + let out = ""; let $lineNumber = 1; let $filename = ${stringify(join(fs.basePath, 'index.edge'))}; try { @@ -1229,14 +1220,14 @@ test.group('Compiler | Processor', (group) => { template.reThrow(error, $filename, $lineNumber); } return out; - `) + }`) ) - }) + }).tags(['compat']) - test('run compiled processor functions for layouts', async ({ assert }) => { + test('run compiled processor functions for layouts', async ({ assert, fs }) => { assert.plan(3) - await fs.add( + await fs.create( 'master.edge', dedent` {{ username }} @@ -1244,7 +1235,7 @@ test.group('Compiler | Processor', (group) => { ` ) - await fs.add( + await fs.create( 'index.edge', dedent` @layout('master') @@ -1275,8 +1266,7 @@ test.group('Compiler | Processor', (group) => { } catch (error) { template.reThrow(error, $filename, $lineNumber); } - return out; - `) + return out;`) ) assert.equal(path, join(fs.basePath, 'index.edge')) }) @@ -1289,10 +1279,13 @@ test.group('Compiler | Processor', (group) => { }, processor ) + compiler.compat = true assert.stringEqual( - compiler.compile('index.edge').template, - normalizeNewLines(dedent`let out = ""; + compiler.compile('index.edge').toString(), + normalizeNewLines(dedent`function anonymous(template,state,$context + ) { + let out = ""; let $lineNumber = 1; let $filename = ${stringify(join(fs.basePath, 'index.edge'))}; try { @@ -1307,19 +1300,19 @@ test.group('Compiler | Processor', (group) => { template.reThrow(error, $filename, $lineNumber); } return out; - `) + }`) ) - }) + }).tags(['compat']) - test('run tag processor function', async ({ assert }) => { - await fs.add( + test('run tag processor function', async ({ assert, fs }) => { + await fs.create( 'modal.edge', dedent` This is a modal ` ) - await fs.add( + await fs.create( 'index.edge', dedent` @hl.modal() @@ -1354,8 +1347,10 @@ test.group('Compiler | Processor', (group) => { }) assert.stringEqual( - compiler.compile('index.edge').template, - normalizeNewLines(dedent`let out = ""; + compiler.compile('index.edge').toString(), + normalizeNewLines(dedent`function anonymous(template,state,$context + ) { + let out = ""; let $lineNumber = 1; let $filename = ${stringify(join(fs.basePath, 'index.edge'))}; try { @@ -1364,7 +1359,7 @@ test.group('Compiler | Processor', (group) => { template.reThrow(error, $filename, $lineNumber); } return out; - `) + }`) ) }) }) diff --git a/test/component.spec.ts b/tests/component.spec.ts similarity index 93% rename from test/component.spec.ts rename to tests/component.spec.ts index 6946fe3..60654bf 100644 --- a/test/component.spec.ts +++ b/tests/component.spec.ts @@ -1,31 +1,33 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { test } from '@japa/runner' -import { join } from 'path' +import './assert_extend.js' + import dedent from 'dedent-js' +import { test } from '@japa/runner' import { EdgeError } from 'edge-error' +import { fileURLToPath } from 'node:url' +import { dirname, join } from 'node:path' import { Filesystem } from '@poppinss/dev-utils' -import { Loader } from '../src/Loader' -import { Compiler } from '../src/Compiler' -import { Template } from '../src/Template' -import { Processor } from '../src/Processor' +import { Loader } from '../src/loader.js' +import { Compiler } from '../src/compiler.js' +import { Template } from '../src/template.js' +import { Processor } from '../src/processor.js' -import { ifTag } from '../src/Tags/If' -import { slotTag } from '../src/Tags/Slot' -import { injectTag } from '../src/Tags/Inject' -import { includeTag } from '../src/Tags/Include' -import { componentTag } from '../src/Tags/Component' -import './assert-extend' +import { ifTag } from '../src/tags/if.js' +import { slotTag } from '../src/tags/slot.js' +import { injectTag } from '../src/tags/inject.js' +import { includeTag } from '../src/tags/include.js' +import { componentTag } from '../src/tags/component.js' -const fs = new Filesystem(join(__dirname, 'views')) +const fs = new Filesystem(join(dirname(fileURLToPath(import.meta.url)), 'views')) const tags = { component: componentTag, slot: slotTag, @@ -376,8 +378,9 @@ test.group('Component | render | errors', (group) => { ` ) + const compatCompiler = new Compiler(loader, tags, processor, { cache: false, compat: true }) const template = new Template( - compiler, + compatCompiler, { raise: (message: string, options: any) => { throw new EdgeError(message, 'E_RUNTIME_EXCEPTION', options) @@ -418,7 +421,7 @@ test.group('Component | context API', (group) => { dedent`

Some content

- @component('modal', needsHandler = true) + @component('modal', { needsHandler: true })

{{ $context.closeHandler }}

@endcomponent ` @@ -452,7 +455,7 @@ test.group('Component | context API', (group) => { dedent`

Some content

- @component('modal', needsHandler = true) + @component('modal', { needsHandler: true })

{{ $context.closeHandler }}

@endcomponent @@ -490,7 +493,7 @@ test.group('Component | context API', (group) => { dedent`

Some content

- @component('modal', needsHandler = true) + @component('modal', { needsHandler: true })

{{ $context.closeHandler }}

@endcomponent @@ -540,7 +543,7 @@ test.group('Component | context API', (group) => {

Some content

@component('wrapper') - @component('modal', needsHandler = true) + @component('modal', { needsHandler: true })

{{ $context.closeHandler }}

@endcomponent @@ -730,7 +733,8 @@ test.group('Component | context API', (group) => { ` ) - const template = new Template(compiler, {}, {}, processor) + const compatCompiler = new Compiler(loader, tags, processor, { cache: false, compat: true }) + const template = new Template(compatCompiler, {}, {}, processor) try { template.render('eval.edge', {}) } catch (error) { @@ -738,5 +742,5 @@ test.group('Component | context API', (group) => { assert.equal(error.filename, join(fs.basePath, 'button.edge')) assert.equal(error.line, 5) } - }) + }).tags(['compat']) }) diff --git a/test/each.spec.ts b/tests/each.spec.ts similarity index 96% rename from test/each.spec.ts rename to tests/each.spec.ts index 12976b1..6d58e9d 100644 --- a/test/each.spec.ts +++ b/tests/each.spec.ts @@ -1,14 +1,14 @@ /* - * edge-js + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ import { test } from '@japa/runner' -import { asyncEach, each } from '../src/utils' +import { asyncEach, each } from '../src/utils.js' test.group('async each', () => { test('iterate over array', async ({ assert }) => { diff --git a/test/edge.spec.ts b/tests/edge.spec.ts similarity index 67% rename from test/edge.spec.ts rename to tests/edge.spec.ts index edc459d..81b9edc 100644 --- a/test/edge.spec.ts +++ b/tests/edge.spec.ts @@ -1,42 +1,36 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import './assert-extend' -import { EOL } from 'os' -import { test } from '@japa/runner' -import { join } from 'path' +import './assert_extend.js' import dedent from 'dedent-js' -import { Filesystem } from '@poppinss/dev-utils' - -import { Edge } from '../src/Edge' -import { GLOBALS } from '../src/Edge/globals' - -const fs = new Filesystem(join(__dirname, 'views')) +import { EOL } from 'node:os' +import { join } from 'node:path' +import { test } from '@japa/runner' -test.group('Edge', (group) => { - group.each.teardown(async () => { - await fs.cleanup() - }) +import { Edge } from '../src/edge/main.js' +import { migrate } from '../src/migrate/plugin.js' +import { edgeGlobals } from '../src/edge/globals.js' - test('mount default disk', async ({ assert }) => { +test.group('Edge', () => { + test('mount default disk', async ({ assert, fs }) => { const edge = new Edge() edge.mount(fs.basePath) assert.deepEqual(edge.loader.mounted, { default: fs.basePath }) }) - test('mount named disk', async ({ assert }) => { + test('mount named disk', async ({ assert, fs }) => { const edge = new Edge() edge.mount('foo', fs.basePath) assert.deepEqual(edge.loader.mounted, { foo: fs.basePath }) }) - test('unmount named disk', async ({ assert }) => { + test('unmount named disk', async ({ assert, fs }) => { const edge = new Edge() edge.mount('foo', fs.basePath) edge.unmount('foo') @@ -46,21 +40,21 @@ test.group('Edge', (group) => { test('register globals', async ({ assert }) => { const edge = new Edge() edge.global('foo', 'bar') - assert.deepEqual(edge.GLOBALS.foo, 'bar') + assert.deepEqual(edge.globals.foo, 'bar') }) test('add a custom tag to the tags list', async ({ assert }) => { const edge = new Edge() class MyTag { - public static tagName = 'mytag' - public static block = true - public static seekable = true - public static compile(): void {} + static tagName = 'mytag' + static block = true + static seekable = true + static compile(): void {} } edge.registerTag(MyTag) - assert.deepEqual(edge.compiler['tags'].mytag, MyTag) + assert.deepEqual(edge['tags'].mytag, MyTag) }) test('invoke tag boot method when registering the tag', async ({ assert }) => { @@ -69,39 +63,43 @@ test.group('Edge', (group) => { const edge = new Edge() class MyTag { - public static tagName = 'mytag' - public static block = true - public static seekable = true - public static compile(): void {} + static tagName = 'mytag' + static block = true + static seekable = true + static compile(): void {} - public static boot(): void { + static boot(): void { assert.isTrue(true) } } edge.registerTag(MyTag) - assert.deepEqual(edge.compiler['tags'].mytag, MyTag) + assert.deepEqual(edge['tags'].mytag, MyTag) }) - test('render a view using the render method', async ({ assert }) => { + test('render a view using the render method', async ({ assert, fs }) => { const edge = new Edge() - await fs.add('foo.edge', 'Hello {{ username }}') + await fs.create('foo.edge', 'Hello {{ username }}') edge.mount(fs.basePath) - assert.equal((await edge.render('foo', { username: 'virk' })).trim(), 'Hello virk') + const output = await edge.render('foo', { username: 'virk' }) + assert.equal(output.trim(), 'Hello virk') }) - test('pass locals to the view context', async ({ assert }) => { + test('pass locals to the view context', async ({ assert, fs }) => { const edge = new Edge() - await fs.add('foo.edge', "Hello {{ username || 'guest' }}") + await fs.create('foo.edge', "Hello {{ username || 'guest' }}") edge.mount(fs.basePath) - const tmpl = edge.getRenderer() + const tmpl = edge.createRenderer() tmpl.share({ username: 'nikk' }) - assert.equal((await tmpl.render('foo', {})).trim(), 'Hello nikk') - assert.equal((await edge.render('foo', {})).trim(), 'Hello guest') + const output = await tmpl.render('foo', {}) + const output1 = await edge.render('foo', {}) + + assert.equal(output.trim(), 'Hello nikk') + assert.equal(output1.trim(), 'Hello guest') }) test('register a template as a string', async ({ assert }) => { @@ -111,10 +109,11 @@ test.group('Edge', (group) => { template: 'Hello {{ username }}', }) - assert.equal((await edge.render('foo', { username: 'virk' })).trim(), 'Hello virk') + const output = await edge.render('foo', { username: 'virk' }) + assert.equal(output.trim(), 'Hello virk') }) - test('register a template on a named disk', async ({ assert }) => { + test('register a template on a named disk', async ({ assert, fs }) => { const edge = new Edge() edge.mount('hello', fs.basePath) @@ -122,35 +121,40 @@ test.group('Edge', (group) => { template: 'Hello {{ username }}', }) - assert.equal((await edge.render('hello::foo', { username: 'virk' })).trim(), 'Hello virk') + const output = await edge.render('hello::foo', { username: 'virk' }) + assert.equal(output.trim(), 'Hello virk') }) - test('clear compiled cache when template is removed', async ({ assert }) => { + test('clear compiled cache when in-memory template is removed', async ({ assert }) => { const edge = new Edge({ cache: true }) edge.registerTemplate('foo', { template: 'Hello {{ username }}', }) - assert.equal((await edge.render('foo', { username: 'virk' })).trim(), 'Hello virk') + + const output = await edge.render('foo', { username: 'virk' }) + assert.equal(output.trim(), 'Hello virk') assert.equal(edge.renderSync('foo', { username: 'virk' }).trim(), 'Hello virk') edge.removeTemplate('foo') edge.registerTemplate('foo', { template: 'Hi {{ username }}', }) - assert.equal((await edge.render('foo', { username: 'virk' })).trim(), 'Hi virk') + + const output1 = await edge.render('foo', { username: 'virk' }) + assert.equal(output1.trim(), 'Hi virk') assert.equal(edge.renderSync('foo', { username: 'virk' }).trim(), 'Hi virk') }) - test('pass absolute path of template to lexer errors', async ({ assert }) => { + test('pass absolute path of template to lexer errors', async ({ assert, fs }) => { assert.plan(1) - await fs.add('foo.edge', '@if(1 + 1)') + await fs.create('foo.edge', '@if(1 + 1)') const edge = new Edge() edge.mount(fs.basePath) try { - await edge.render('foo', false) + await edge.render('foo') } catch ({ stack }) { assert.equal( stack.split('\n')[1].trim(), @@ -159,15 +163,15 @@ test.group('Edge', (group) => { } }) - test('pass absolute path of template to parser errors', async ({ assert }) => { + test('pass absolute path of template to parser errors', async ({ assert, fs }) => { assert.plan(1) - await fs.add('foo.edge', 'Hello {{ a,:b }}') + await fs.create('foo.edge', 'Hello {{ a,:b }}') const edge = new Edge() edge.mount(fs.basePath) try { - await edge.render('foo', false) + await edge.render('foo') } catch ({ stack }) { assert.equal( stack.split('\n')[1].trim(), @@ -176,53 +180,56 @@ test.group('Edge', (group) => { } }) - test('pass absolute path of layout to lexer errors', async ({ assert }) => { + test('pass absolute path of layout to lexer errors', async ({ assert, fs }) => { assert.plan(1) - await fs.add('foo.edge', "@layout('bar')") - await fs.add('bar.edge', '@if(username)') + await fs.create('foo.edge', "@layout('bar')") + await fs.create('bar.edge', '@if(username)') const edge = new Edge() edge.mount(fs.basePath) + edge.use(migrate) try { - await edge.render('foo', false) + await edge.render('foo') } catch ({ stack }) { assert.equal( stack.split('\n')[1].trim(), `at anonymous (${join(fs.basePath, 'bar.edge')}:1:4)` ) } - }) + }).tags(['compat']) - test('pass absolute path of layout to parser errors', async ({ assert }) => { + test('pass absolute path of layout to parser errors', async ({ assert, fs }) => { assert.plan(1) - await fs.add('foo.edge', "@layout('bar')") - await fs.add('bar.edge', '{{ a:b }}') + await fs.create('foo.edge', "@layout('bar')") + await fs.create('bar.edge', '{{ a:b }}') const edge = new Edge() edge.mount(fs.basePath) + edge.use(migrate) try { - await edge.render('foo', false) + await edge.render('foo') } catch ({ stack }) { assert.equal( stack.split('\n')[1].trim(), `at anonymous (${join(fs.basePath, 'bar.edge')}:1:3)` ) } - }) + }).tags(['compat']) - test('pass absolute path of partial to lexer errors', async ({ assert }) => { + test('pass absolute path of partial to lexer errors', async ({ assert, fs }) => { assert.plan(1) - await fs.add('foo.edge', "@include('bar')") - await fs.add('bar.edge', '@if(username)') + await fs.create('foo.edge', "@include('bar')") + await fs.create('bar.edge', '@if(username)') const edge = new Edge() edge.mount(fs.basePath) try { - await edge.render('foo', false) + await edge.render('foo') } catch ({ stack }) { + console.log(stack) assert.equal( stack.split('\n')[1].trim(), `at anonymous (${join(fs.basePath, 'bar.edge')}:1:4)` @@ -230,16 +237,16 @@ test.group('Edge', (group) => { } }) - test('pass absolute path of partial to parser errors', async ({ assert }) => { + test('pass absolute path of partial to parser errors', async ({ assert, fs }) => { assert.plan(1) - await fs.add('foo.edge', "@include('bar')") - await fs.add('bar.edge', '{{ a:b }}') + await fs.create('foo.edge', "@include('bar')") + await fs.create('bar.edge', '{{ a:b }}') const edge = new Edge() edge.mount(fs.basePath) try { - await edge.render('foo', false) + await edge.render('foo') } catch ({ stack }) { assert.equal( stack.split('\n')[1].trim(), @@ -248,16 +255,16 @@ test.group('Edge', (group) => { } }) - test('pass absolute path of component to lexer errors', async ({ assert }) => { + test('pass absolute path of component to lexer errors', async ({ assert, fs }) => { assert.plan(1) - await fs.add('foo.edge', "@!component('bar')") - await fs.add('bar.edge', '@if(username)') + await fs.create('foo.edge', "@!component('bar')") + await fs.create('bar.edge', '@if(username)') const edge = new Edge() edge.mount(fs.basePath) try { - await edge.render('foo', false) + await edge.render('foo') } catch ({ stack }) { assert.equal( stack.split('\n')[1].trim(), @@ -266,16 +273,16 @@ test.group('Edge', (group) => { } }) - test('pass absolute path of component to parser errors', async ({ assert }) => { + test('pass absolute path of component to parser errors', async ({ assert, fs }) => { assert.plan(1) - await fs.add('foo.edge', "@!component('bar')") - await fs.add('bar.edge', '{{ a:b }}') + await fs.create('foo.edge', "@!component('bar')") + await fs.create('bar.edge', '{{ a:b }}') const edge = new Edge() edge.mount(fs.basePath) try { - await edge.render('foo', false) + await edge.render('foo') } catch ({ stack }) { assert.equal( stack.split('\n')[1].trim(), @@ -284,7 +291,7 @@ test.group('Edge', (group) => { } }) - test('register and call plugins before rendering a view', async ({ assert }) => { + test('register and call plugins before rendering a view', async ({ assert, fs }) => { assert.plan(3) const edge = new Edge() @@ -300,10 +307,11 @@ test.group('Edge', (group) => { template: 'Hello {{ username }}', }) - assert.equal((await edge.render('hello::foo', { username: 'virk' })).trim(), 'Hello virk') + const output = await edge.render('hello::foo', { username: 'virk' }) + assert.equal(output.trim(), 'Hello virk') }) - test('do not run plugins until a view is rendered', async ({ assert }) => { + test('do not run plugins until a view is rendered', async ({ assert, fs }) => { assert.plan(0) const edge = new Edge() @@ -320,8 +328,8 @@ test.group('Edge', (group) => { }) }) - test('run plugins only once', async ({ assert }) => { - assert.plan(5) + test('run plugins only once', async ({ assert, fs }) => { + assert.plan(2) const edge = new Edge() edge.use(($edge) => { @@ -336,13 +344,13 @@ test.group('Edge', (group) => { template: 'Hello {{ username }}', }) - assert.equal((await edge.render('hello::foo', { username: 'virk' })).trim(), 'Hello virk') - assert.equal((await edge.render('hello::foo', { username: 'virk' })).trim(), 'Hello virk') - assert.equal((await edge.render('hello::foo', { username: 'virk' })).trim(), 'Hello virk') + await edge.render('hello::foo', { username: 'virk' }) + await edge.render('hello::foo', { username: 'virk' }) + await edge.render('hello::foo', { username: 'virk' }) }) - test('run recurring plugins again and again', async ({ assert }) => { - assert.plan(9) + test('run recurring plugins again and again', async ({ assert, fs }) => { + assert.plan(6) const edge = new Edge() edge.use( @@ -360,12 +368,12 @@ test.group('Edge', (group) => { template: 'Hello {{ username }}', }) - assert.equal((await edge.render('hello::foo', { username: 'virk' })).trim(), 'Hello virk') - assert.equal((await edge.render('hello::foo', { username: 'virk' })).trim(), 'Hello virk') - assert.equal((await edge.render('hello::foo', { username: 'virk' })).trim(), 'Hello virk') + await edge.render('hello::foo', { username: 'virk' }) + await edge.render('hello::foo', { username: 'virk' }) + await edge.render('hello::foo', { username: 'virk' }) }) - test('hook into renderer instance', async ({ assert }) => { + test('hook into renderer instance', async ({ assert, fs }) => { const edge = new Edge() edge.onRender((renderer) => { @@ -377,9 +385,44 @@ test.group('Edge', (group) => { template: 'Hello {{ foo }}', }) - assert.equal((await edge.render('hello::foo')).trim(), 'Hello bar') - assert.equal((await edge.render('hello::foo')).trim(), 'Hello bar') - assert.equal((await edge.render('hello::foo')).trim(), 'Hello bar') + const output = await edge.render('hello::foo') + assert.equal(output.trim(), 'Hello bar') + }) + + test('render components as tags', async ({ assert, fs }) => { + const edge = new Edge() + await fs.create('foo.edge', '@!foo({ username })') + await fs.create('components/foo.edge', 'Hello {{ username }}') + + edge.mount(fs.basePath) + const output = await edge.render('foo', { username: 'virk' }) + assert.equal(output.trim(), 'Hello virk') + }) + + test('refresh components list on each render', async ({ assert, fs }) => { + const edge = new Edge() + await fs.create('foo.edge', '@!foo({ username })') + edge.mount(fs.basePath) + + const output = await edge.render('foo', { username: 'virk' }) + assert.equal(output.trim(), '@!foo({ username })') + + await fs.create('components/foo.edge', 'Hello {{ username }}') + const output1 = await edge.render('foo', { username: 'virk' }) + assert.equal(output1.trim(), 'Hello virk') + }) + + test('do not refresh list when cache mode is enabled', async ({ assert, fs }) => { + const edge = new Edge({ cache: true }) + await fs.create('foo.edge', '@!foo({ username })') + edge.mount(fs.basePath) + + const output = await edge.render('foo', { username: 'virk' }) + assert.equal(output.trim(), '@!foo({ username })') + + await fs.create('components/foo.edge', 'Hello {{ username }}') + const output1 = await edge.render('foo', { username: 'virk' }) + assert.equal(output1.trim(), '@!foo({ username })') }) }) @@ -444,17 +487,17 @@ test.group('Edge | regression', () => { test('do not escape when using safe global method', async ({ assert }) => { const edge = new Edge() - Object.keys(GLOBALS).forEach((key) => edge.global(key, GLOBALS[key])) + edge.use(migrate) edge.registerTemplate('eval', { template: 'Hello {{ safe(username) }}', }) assert.equal(await edge.render('eval', { username: '

virk

' }), 'Hello

virk

') - }) + }).tags(['compat']) test('truncate string by characters', async ({ assert }) => { const edge = new Edge() - Object.keys(GLOBALS).forEach((key) => edge.global(key, GLOBALS[key])) + edge.globals = edgeGlobals edge.registerTemplate('eval', { template: '{{{ truncate(text, 10) }}}', @@ -467,7 +510,7 @@ test.group('Edge | regression', () => { test('truncate string by characters in strict mode', async ({ assert }) => { const edge = new Edge() - Object.keys(GLOBALS).forEach((key) => edge.global(key, GLOBALS[key])) + edge.globals = edgeGlobals edge.registerTemplate('eval', { template: '{{{ truncate(text, 10, { strict: true }) }}}', @@ -480,7 +523,7 @@ test.group('Edge | regression', () => { test('define custom suffix for truncate', async ({ assert }) => { const edge = new Edge() - Object.keys(GLOBALS).forEach((key) => edge.global(key, GLOBALS[key])) + edge.globals = edgeGlobals edge.registerTemplate('eval', { template: '{{{ truncate(text, 10, { suffix: ". more" }) }}}', @@ -493,7 +536,7 @@ test.group('Edge | regression', () => { test('generate string excerpt', async ({ assert }) => { const edge = new Edge() - Object.keys(GLOBALS).forEach((key) => edge.global(key, GLOBALS[key])) + edge.globals = edgeGlobals edge.registerTemplate('eval', { template: '{{{ excerpt(text, 10) }}}', @@ -506,7 +549,7 @@ test.group('Edge | regression', () => { test('excerpt remove in-between tag', async ({ assert }) => { const edge = new Edge() - Object.keys(GLOBALS).forEach((key) => edge.global(key, GLOBALS[key])) + edge.globals = edgeGlobals edge.registerTemplate('eval', { template: '{{{ excerpt(text, 10) }}}', @@ -521,7 +564,7 @@ test.group('Edge | regression', () => { test('generate excerpt in strict mode', async ({ assert }) => { const edge = new Edge() - Object.keys(GLOBALS).forEach((key) => edge.global(key, GLOBALS[key])) + edge.globals = edgeGlobals edge.registerTemplate('eval', { template: '{{{ excerpt(text, 10, { strict: true }) }}}', @@ -536,7 +579,7 @@ test.group('Edge | regression', () => { test('add custom suffix for excerpt', async ({ assert }) => { const edge = new Edge() - Object.keys(GLOBALS).forEach((key) => edge.global(key, GLOBALS[key])) + edge.globals = edgeGlobals edge.registerTemplate('eval', { template: '{{{ excerpt(text, 10, { suffix: ". more" }) }}}', @@ -551,7 +594,7 @@ test.group('Edge | regression', () => { test('convert newline to br tags', async ({ assert }) => { const edge = new Edge() - Object.keys(GLOBALS).forEach((key) => edge.global(key, GLOBALS[key])) + edge.globals = edgeGlobals /** * Intentionally using `EOL`, so that we can test that in windows @@ -566,7 +609,7 @@ test.group('Edge | regression', () => { test('escape user input except the new lines', async ({ assert }) => { const edge = new Edge() - Object.keys(GLOBALS).forEach((key) => edge.global(key, GLOBALS[key])) + edge.use(migrate) /** * Intentionally using `EOL`, so that we can test that in windows @@ -580,11 +623,11 @@ test.group('Edge | regression', () => { await edge.render('eval', { text: `Hello${EOL}world` }), 'Hello
<strong>world</strong>' ) - }) + }).tags(['compat']) test('stringify data structures', async ({ assert }) => { const edge = new Edge() - Object.keys(GLOBALS).forEach((key) => edge.global(key, GLOBALS[key])) + edge.use(migrate) /** * Intentionally using `EOL`, so that we can test that in windows @@ -598,5 +641,5 @@ test.group('Edge | regression', () => { await edge.render('eval'), '{"user":{"username":"virk"}}' ) - }) + }).tags(['compat']) }) diff --git a/test/filename.spec.ts b/tests/filename.spec.ts similarity index 75% rename from test/filename.spec.ts rename to tests/filename.spec.ts index 9ffcdd5..d47bc31 100644 --- a/test/filename.spec.ts +++ b/tests/filename.spec.ts @@ -1,31 +1,24 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { test } from '@japa/runner' -import { join } from 'path' +import './assert_extend.js' import dedent from 'dedent-js' -import { Filesystem } from '@poppinss/dev-utils' - -import { Edge } from '../src/Edge' -import { normalizeNewLines } from '../test-helpers' - -import './assert-extend' - -const fs = new Filesystem(join(__dirname, 'views')) +import { join } from 'node:path' +import { test } from '@japa/runner' -test.group('Template FileName', (group) => { - group.each.teardown(async () => { - await fs.cleanup() - }) +import { Edge } from '../src/edge/main.js' +import { migrate } from '../src/migrate/plugin.js' +import { normalizeNewLines } from '../tests_helpers/index.js' - test('print file absolute path', async ({ assert }) => { - await fs.add('foo.edge', '{{ $filename }}') +test.group('Template FileName', () => { + test('print file absolute path', async ({ assert, fs }) => { + await fs.create('foo.edge', '{{ $filename }}') const edge = new Edge() edge.mount(fs.basePath) @@ -34,15 +27,15 @@ test.group('Template FileName', (group) => { assert.equal(output.trim(), join(fs.basePath, 'foo.edge')) }) - test('print file absolute path inside a partial', async ({ assert }) => { - await fs.add( + test('print file absolute path inside a partial', async ({ assert, fs }) => { + await fs.create( 'foo.edge', dedent` @include('bar') {{ $filename }} ` ) - await fs.add('bar.edge', '{{ $filename }}') + await fs.create('bar.edge', '{{ $filename }}') const edge = new Edge() edge.mount(fs.basePath) @@ -58,8 +51,8 @@ test.group('Template FileName', (group) => { ) }) - test('print file absolute path inside a layout', async ({ assert }) => { - await fs.add( + test('print file absolute path inside a layout', async ({ assert, fs }) => { + await fs.create( 'foo.edge', dedent` @layout('master') @@ -70,7 +63,7 @@ test.group('Template FileName', (group) => { ` ) - await fs.add( + await fs.create( 'master.edge', dedent` @section('content') @@ -80,6 +73,7 @@ test.group('Template FileName', (group) => { ) const edge = new Edge() + edge.use(migrate) edge.mount(fs.basePath) const output = await edge.render('foo', {}) @@ -91,17 +85,17 @@ test.group('Template FileName', (group) => { ${join(fs.basePath, 'foo.edge')} `) ) - }) + }).tags(['compat']) - test('print file absolute path inside a partial', async ({ assert }) => { - await fs.add( + test('print file absolute path inside a partial', async ({ assert, fs }) => { + await fs.create( 'foo.edge', dedent` @include('bar') {{ $filename }} ` ) - await fs.add('bar.edge', '{{ $filename }}') + await fs.create('bar.edge', '{{ $filename }}') const edge = new Edge() edge.mount(fs.basePath) @@ -117,8 +111,8 @@ test.group('Template FileName', (group) => { ) }) - test('print file absolute path inside a component', async ({ assert }) => { - await fs.add( + test('print file absolute path inside a component', async ({ assert, fs }) => { + await fs.create( 'foo.edge', dedent` @component('button') @@ -129,7 +123,7 @@ test.group('Template FileName', (group) => { ` ) - await fs.add( + await fs.create( 'button.edge', dedent` {{{ await $slots.text() }}} diff --git a/tests/fixtures.spec.ts b/tests/fixtures.spec.ts new file mode 100644 index 0000000..282550f --- /dev/null +++ b/tests/fixtures.spec.ts @@ -0,0 +1,157 @@ +/* + * edge.js-parser + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import './assert_extend.js' + +import dedent from 'dedent-js' +import { test } from '@japa/runner' +import { fileURLToPath } from 'node:url' +import { dirname, join } from 'node:path' +import { readdirSync, readFileSync, statSync } from 'node:fs' + +import { Loader } from '../src/loader.js' +import * as tags from '../src/tags/main.js' +import { Template } from '../src/template.js' +import { Compiler } from '../src/compiler.js' +import { Processor } from '../src/processor.js' +import * as compatTags from '../src/migrate/tags/main.js' + +import { normalizeNewLines, normalizeFilename } from '../tests_helpers/index.js' +const basePath = join(dirname(fileURLToPath(import.meta.url)), '../fixtures') + +const loader = new Loader() +const processor = new Processor() + +loader.mount('default', basePath) + +test.group('Fixtures', (group) => { + group.setup(() => { + Object.keys(tags).forEach((tag) => { + tags[tag as keyof typeof tags].boot?.(Template) + }) + Object.keys(compatTags).forEach((tag) => { + compatTags[tag as keyof typeof compatTags].boot?.(Template) + }) + }) + + const dirs = readdirSync(basePath).filter((file) => statSync(join(basePath, file)).isDirectory()) + const compiler = new Compiler( + loader, + { + ...tags, + ...compatTags, + }, + processor, + { async: false } + ) + + dirs.forEach((dir) => { + const dirBasePath = join(basePath, dir) + const compatMode = dir.endsWith('-compat') + + test(dir, ({ assert }) => { + const template = new Template(compiler, {}, {}, processor) + compiler.compat = compatMode + + /** + * Compiled output + */ + const compiledTemplate = compiler.compile(`${dir}/index.edge`) + const expectedCompiled = normalizeNewLines( + readFileSync(join(dirBasePath, 'compiled.js'), 'utf-8') + ) + assert.stringEqual( + compiledTemplate.toString(), + dedent`function anonymous(template,state,$context + ) { + ${expectedCompiled + .split('\n') + .map((line) => normalizeFilename(dirBasePath, line)) + .join('\n')} + }` + ) + + /** + * Render output + */ + const out = readFileSync(join(dirBasePath, 'index.txt'), 'utf-8') + const state = readFileSync(join(dirBasePath, 'index.json'), 'utf-8') + const output = template.render(`${dir}/index.edge`, JSON.parse(state)) as string + const outputRaw = template.renderRaw( + readFileSync(join(dirBasePath, 'index.edge'), 'utf-8'), + JSON.parse(state) + ) + assert.stringEqual(output.trim(), out) + assert.stringEqual(outputRaw.trim(), out) + }).tags(compatMode ? ['compat'] : []) + }) +}) + +test.group('Fixtures | Cache', (group) => { + group.setup(() => { + Object.keys(tags).forEach((tag) => { + tags[tag as keyof typeof tags].boot?.(Template) + }) + Object.keys(compatTags).forEach((tag) => { + compatTags[tag as keyof typeof compatTags].boot?.(Template) + }) + }) + + const dirs = readdirSync(basePath).filter((file) => statSync(join(basePath, file)).isDirectory()) + const compiler = new Compiler( + loader, + { + ...tags, + ...compatTags, + }, + processor, + { async: false, cache: true } + ) + + dirs.forEach((dir) => { + const dirBasePath = join(basePath, dir) + const compatMode = dir.endsWith('-compat') + + test(dir, ({ assert }) => { + const template = new Template(compiler, {}, {}, processor) + compiler.compat = compatMode + + /** + * Compiled output + */ + const compiledTemplate = compiler.compile(`${dir}/index.edge`) + const expectedCompiled = normalizeNewLines( + readFileSync(join(dirBasePath, 'compiled.js'), 'utf-8') + ) + assert.stringEqual( + compiledTemplate.toString(), + dedent`function anonymous(template,state,$context + ) { + ${expectedCompiled + .split('\n') + .map((line) => normalizeFilename(dirBasePath, line)) + .join('\n')} + }` + ) + + /** + * Render output + */ + const out = readFileSync(join(dirBasePath, 'index.txt'), 'utf-8') + const state = readFileSync(join(dirBasePath, 'index.json'), 'utf-8') + const output = template.render(`${dir}/index.edge`, JSON.parse(state)) as string + const outputRaw = template.renderRaw( + readFileSync(join(dirBasePath, 'index.edge'), 'utf-8'), + JSON.parse(state) + ) + assert.stringEqual(output.trim(), out) + assert.stringEqual(outputRaw.trim(), out) + }).tags(compatMode ? ['compat'] : []) + }) +}) diff --git a/tests/globals/attrs.spec.ts b/tests/globals/attrs.spec.ts new file mode 100644 index 0000000..1bc5755 --- /dev/null +++ b/tests/globals/attrs.spec.ts @@ -0,0 +1,355 @@ +/* + * edge-js + * + * (c) Edge + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import '../assert_extend.js' +import dedent from 'dedent-js' +import { test } from '@japa/runner' +import path, { join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { Filesystem } from '@poppinss/dev-utils' + +import { Loader } from '../../src/loader.js' +import { Template } from '../../src/template.js' +import { Compiler } from '../../src/compiler.js' +import { slotTag } from '../../src/tags/slot.js' +import { Processor } from '../../src/processor.js' +import { includeTag } from '../../src/tags/include.js' +import { edgeGlobals } from '../../src/edge/globals.js' +import { componentTag } from '../../src/tags/component.js' + +const tags = { slot: slotTag, component: componentTag, include: includeTag } +const fs = new Filesystem(join(path.dirname(fileURLToPath(import.meta.url)), 'views')) + +const loader = new Loader() +loader.mount('default', fs.basePath) + +test.group('Template | toAttributes', () => { + test('serialize object to HTML attributes', async ({ assert }) => { + const processor = new Processor() + const compiler = new Compiler(loader, tags, processor, { cache: false }) + const template = new Template(compiler, edgeGlobals, {}, processor) + + const html = await template.renderRaw( + dedent` + + `, + { + hasError: false, + } + ) + + assert.stringEqual( + html, + dedent` + + ` + ) + }) + + test('allow properties with no values', async ({ assert }) => { + const processor = new Processor() + const compiler = new Compiler(loader, tags, processor, { cache: false }) + const template = new Template(compiler, edgeGlobals, {}, processor) + + const html = await template.renderRaw( + dedent` + + `, + {} + ) + + assert.stringEqual( + html, + dedent` + + ` + ) + }) + + test('handle overloaded boolean properties', async ({ assert }) => { + const processor = new Processor() + const compiler = new Compiler(loader, tags, processor, { cache: false }) + const template = new Template(compiler, edgeGlobals, {}, processor) + + const html = await template.renderRaw( + dedent` + + `, + {} + ) + + assert.stringEqual( + html, + dedent` + + ` + ) + }) + + test('handle overloaded boolean properties with explicit value', async ({ assert }) => { + const processor = new Processor() + const compiler = new Compiler(loader, tags, processor, { cache: false }) + const template = new Template(compiler, edgeGlobals, {}, processor) + + const html = await template.renderRaw( + dedent` + + `, + {} + ) + + assert.stringEqual( + html, + dedent` + + ` + ) + }) + + test('allow non-standard attributes', async ({ assert }) => { + const processor = new Processor() + const compiler = new Compiler(loader, tags, processor, { cache: false }) + const template = new Template(compiler, edgeGlobals, {}, processor) + + const html = await template.renderRaw( + dedent` + + `, + { + hasError: false, + } + ) + + assert.stringEqual( + html, + dedent` + + ` + ) + }) + + test('define comma separated values', async ({ assert }) => { + const processor = new Processor() + const compiler = new Compiler(loader, tags, processor, { cache: false }) + const template = new Template(compiler, edgeGlobals, {}, processor) + + const html = await template.renderRaw( + dedent` + + `, + {} + ) + + assert.stringEqual( + html, + dedent` + + ` + ) + }) + + test('define alpine js attributes', async ({ assert }) => { + const processor = new Processor() + const compiler = new Compiler(loader, tags, processor, { cache: false }) + const template = new Template(compiler, edgeGlobals, {}, processor) + + const html = await template.renderRaw( + dedent` +
+
+ `, + {} + ) + + assert.stringEqual( + html, + dedent` +
+
+ ` + ) + }) + + test('define alpine js boolean attributes', async ({ assert }) => { + const processor = new Processor() + const compiler = new Compiler(loader, tags, processor, { cache: false }) + const template = new Template(compiler, edgeGlobals, {}, processor) + + const html = await template.renderRaw( + dedent` +
+
+ `, + {} + ) + + assert.stringEqual( + html, + dedent` +
+
+ ` + ) + }) + + test('define alpine js event listeners', async ({ assert }) => { + const processor = new Processor() + const compiler = new Compiler(loader, tags, processor, { cache: false }) + const template = new Template(compiler, edgeGlobals, {}, processor) + + const html = await template.renderRaw( + dedent` +
+
+ `, + {} + ) + + assert.stringEqual( + html, + dedent` +
+
+ ` + ) + }) + + test('define alpinejs x-bind properties', async ({ assert }) => { + const processor = new Processor() + const compiler = new Compiler(loader, tags, processor, { cache: false }) + const template = new Template(compiler, edgeGlobals, {}, processor) + + const html = await template.renderRaw( + dedent` +
+
+ `, + {} + ) + + assert.stringEqual( + html, + dedent` +
+
+ ` + ) + }) + + test('define alpinejs transition properties', async ({ assert }) => { + const processor = new Processor() + const compiler = new Compiler(loader, tags, processor, { cache: false }) + const template = new Template(compiler, edgeGlobals, {}, processor) + + const html = await template.renderRaw( + dedent` +
+
+ `, + {} + ) + + assert.stringEqual( + html, + dedent` +
+
+ ` + ) + }) +}) diff --git a/tests/loader.spec.ts b/tests/loader.spec.ts new file mode 100644 index 0000000..b725291 --- /dev/null +++ b/tests/loader.spec.ts @@ -0,0 +1,354 @@ +/* + * edge.js + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { join } from 'node:path' +import { test } from '@japa/runner' +import { getDirname } from '@poppinss/utils' + +import { Loader } from '../src/loader.js' + +const dirnameEsm = getDirname(import.meta.url) + +test.group('Loader', () => { + test('mount path with a name', ({ assert, fs }) => { + const loader = new Loader() + loader.mount('default', fs.basePath) + assert.deepEqual(loader.mounted, { default: fs.basePath }) + }) + + test('unmount path with a name', ({ assert, fs }) => { + const loader = new Loader() + loader.mount('default', fs.basePath) + loader.unmount('default') + assert.deepEqual(loader.mounted, {}) + }) + + test('throw exception when resolving path from undefined location', ({ assert }) => { + const loader = new Loader() + const fn = () => loader.resolve('foo') + assert.throws(fn, '"default" namespace is not mounted') + }) + + test('resolve template for the default disk', async ({ assert, fs }) => { + await fs.create('foo.edge', 'Hello world') + + const loader = new Loader() + loader.mount('default', fs.basePath) + + const { template } = loader.resolve('foo') + assert.equal(template.trim(), 'Hello world') + }) + + test('raise error when template is missing', async ({ assert, fs }) => { + const loader = new Loader() + loader.mount('default', fs.basePath) + + const fn = () => loader.resolve('foo') + assert.throws( + fn, + `Cannot resolve "${join(fs.basePath, 'foo.edge')}". Make sure the file exists` + ) + }) + + test('resolve template with extension', async ({ assert, fs }) => { + await fs.create('foo.edge', 'Hello world') + + const loader = new Loader() + loader.mount('default', fs.basePath) + + const { template } = loader.resolve('foo.edge') + assert.equal(template.trim(), 'Hello world') + }) + + test('resolve template from a named disk', async ({ assert, fs }) => { + await fs.create('foo.edge', 'Hello world') + + const loader = new Loader() + loader.mount('users', fs.basePath) + + const { template } = loader.resolve('users::foo.edge') + assert.equal(template.trim(), 'Hello world') + }) + + test('do not replace edge within the template path name', async ({ assert, fs }) => { + const loader = new Loader() + loader.mount('default', fs.basePath) + + const templatePath = loader.makePath('edge-partial.edge') + assert.equal(templatePath, join(fs.basePath, 'edge-partial.edge')) + }) + + test('do not replace edge within the template path seperator', async ({ assert, fs }) => { + const loader = new Loader() + loader.mount('default', fs.basePath) + + const templatePath = loader.makePath('partial/edge') + assert.equal(templatePath, join(fs.basePath, 'partial/edge.edge')) + }) + + test('do not replace edge within the template path seperator with extension', async ({ + assert, + fs, + }) => { + const loader = new Loader() + loader.mount('default', fs.basePath) + + const templatePath = loader.makePath('partial/edge.edge') + assert.equal(templatePath, join(fs.basePath, 'partial/edge.edge')) + }) + + test('do not replace edge within the template path seperator with named disk', async ({ + assert, + fs, + }) => { + const loader = new Loader() + loader.mount('users', fs.basePath) + + const templatePath = loader.makePath('users::partial/edge.edge') + assert.equal(templatePath, join(fs.basePath, 'partial/edge.edge')) + }) + + test('pre register templates with a key', async ({ assert }) => { + const loader = new Loader() + loader.register('my-view', { + template: 'Hello world', + }) + + const { template } = loader.resolve('my-view') + assert.equal(template.trim(), 'Hello world') + }) + + test('remove registered template', async ({ assert }) => { + const loader = new Loader() + loader.register('my-view', { + template: 'Hello world', + }) + loader.mount('default', dirnameEsm) + + const { template } = loader.resolve('my-view') + assert.equal(template.trim(), 'Hello world') + + loader.remove('my-view') + assert.throws( + () => loader.resolve('my-view'), + `Cannot resolve "${join(dirnameEsm, 'my-view.edge')}". Make sure the file exists` + ) + }) + + test('pre registering duplicate templates must raise an error', async ({ assert }) => { + const loader = new Loader() + loader.register('my-view', { template: 'Hello world' }) + const fn = () => loader.register('my-view', { template: 'Hello world' }) + + assert.throws(fn, 'Cannot override previously registered "my-view" template') + }) +}) + +test.group('Loader | listComponents', () => { + test('get components for the default disk', async ({ assert, fs }) => { + await fs.create('components/foo.edge', 'Hello world') + + const loader = new Loader() + loader.mount('default', fs.basePath) + + const componentsList = loader.listComponents() + assert.deepEqual(componentsList, [ + { + diskName: 'default', + components: [ + { + componentName: 'components/foo', + tagName: 'foo', + }, + ], + }, + ]) + }) + + test('get components from nested directories', async ({ assert, fs }) => { + await fs.create('components/modal/root.edge', 'Hello world') + + const loader = new Loader() + loader.mount('default', fs.basePath) + + const componentsList = loader.listComponents() + assert.deepEqual(componentsList, [ + { + diskName: 'default', + components: [ + { + componentName: 'components/modal/root', + tagName: 'modal.root', + }, + ], + }, + ]) + }) + + test('get components from nested directories from a named disk', async ({ assert, fs }) => { + await fs.create('components/modal/root.edge', 'Hello world') + + const loader = new Loader() + loader.mount('uikit', fs.basePath) + + const componentsList = loader.listComponents() + assert.deepEqual(componentsList, [ + { + diskName: 'uikit', + components: [ + { + componentName: 'uikit::components/modal/root', + tagName: 'uikit.modal.root', + }, + ], + }, + ]) + }) + + test('rename nested components saved in index.edge file', async ({ assert, fs }) => { + await fs.create('components/modal/index.edge', 'Hello world') + await fs.create('components/index.edge', 'Hello world') + + const loader = new Loader() + loader.mount('uikit', fs.basePath) + + const componentsList = loader.listComponents() + assert.deepEqual(componentsList, [ + { + diskName: 'uikit', + components: [ + { + componentName: 'uikit::components/index', + tagName: 'uikit.index', + }, + { + componentName: 'uikit::components/modal/index', + tagName: 'uikit.modal', + }, + ], + }, + ]) + }) + + test('rename nested components saved in index.edge file from default disk', async ({ + assert, + fs, + }) => { + await fs.create('components/modal/index.edge', 'Hello world') + await fs.create('components/index.edge', 'Hello world') + + const loader = new Loader() + loader.mount('default', fs.basePath) + + const componentsList = loader.listComponents() + assert.deepEqual(componentsList, [ + { + diskName: 'default', + components: [ + { + componentName: 'components/index', + tagName: 'index', + }, + { + componentName: 'components/modal/index', + tagName: 'modal', + }, + ], + }, + ]) + }) + + test('get in-memory templates as components', async ({ assert, fs }) => { + await fs.create('components/foo.edge', 'Hello world') + + const loader = new Loader() + loader.mount('default', fs.basePath) + loader.register('uikit/button', { + template: ``, + }) + + const componentsList = loader.listComponents() + assert.deepEqual(componentsList, [ + { + diskName: 'default', + components: [ + { + componentName: 'uikit/button', + tagName: 'uikit.button', + }, + { + componentName: 'components/foo', + tagName: 'foo', + }, + ], + }, + ]) + }) +}) + +test.group('Loader | listComponents', () => { + test('get templates for the default disk', async ({ assert, fs }) => { + await fs.create('components/foo.edge', 'Hello world') + await fs.create('header.edge', 'Hello world') + await fs.create('footer.edge', 'Hello world') + + const loader = new Loader() + loader.mount('default', fs.basePath) + + const componentsList = loader.listTemplates() + assert.deepEqual(componentsList, [ + { + diskName: 'default', + templates: ['components/foo', 'footer', 'header'], + }, + ]) + }) + + test('get templates for the named disk', async ({ assert, fs }) => { + await fs.create('components/foo.edge', 'Hello world') + await fs.create('header.edge', 'Hello world') + await fs.create('footer.edge', 'Hello world') + + const loader = new Loader() + loader.mount('elegant', fs.basePath) + + const componentsList = loader.listTemplates() + assert.deepEqual(componentsList, [ + { + diskName: 'elegant', + templates: ['elegant::components/foo', 'elegant::footer', 'elegant::header'], + }, + ]) + }) + + test('get in-memory templates', async ({ assert, fs }) => { + await fs.create('components/foo.edge', 'Hello world') + await fs.create('header.edge', 'Hello world') + await fs.create('footer.edge', 'Hello world') + + const loader = new Loader() + loader.mount('elegant', fs.basePath) + loader.mount('default', join(fs.basePath, 'foo')) + loader.register('uibutton', { + template: '', + }) + + const componentsList = loader.listTemplates() + assert.deepEqual(componentsList, [ + { + diskName: 'elegant', + templates: ['elegant::components/foo', 'elegant::footer', 'elegant::header'], + }, + { + diskName: 'default', + templates: ['uibutton'], + }, + ]) + }) +}) diff --git a/test/new-error.spec.ts b/tests/new_error.spec.ts similarity index 85% rename from test/new-error.spec.ts rename to tests/new_error.spec.ts index 6608e2c..f12b703 100644 --- a/test/new-error.spec.ts +++ b/tests/new_error.spec.ts @@ -1,27 +1,28 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { test } from '@japa/runner' -import { join } from 'path' import dedent from 'dedent-js' +import { test } from '@japa/runner' +import { fileURLToPath } from 'node:url' +import { dirname, join } from 'node:path' import { Filesystem } from '@poppinss/dev-utils' -import { Loader } from '../src/Loader' -import { Compiler } from '../src/Compiler' -import { newError } from '../src/Tags' -import { Processor } from '../src/Processor' -import { Template } from '../src/Template' +import { Loader } from '../src/loader.js' +import { Compiler } from '../src/compiler.js' +import { newError } from '../src/tags/main.js' +import { Processor } from '../src/processor.js' +import { Template } from '../src/template.js' -import './assert-extend' +import './assert_extend.js' const tags = { newError } -const fs = new Filesystem(join(__dirname, 'views')) +const fs = new Filesystem(join(dirname(fileURLToPath(import.meta.url)), 'views')) const loader = new Loader() loader.mount('default', fs.basePath) @@ -29,7 +30,6 @@ loader.mount('default', fs.basePath) test.group('New Error', (group) => { group.each.teardown(async () => { await fs.cleanup() - Template.hydrate() }) test('raise an exception', async ({ assert }) => { diff --git a/test/newline-fixtures.spec.ts b/tests/newline_fixtures.spec.ts similarity index 51% rename from test/newline-fixtures.spec.ts rename to tests/newline_fixtures.spec.ts index 84a1929..451a10c 100644 --- a/test/newline-fixtures.spec.ts +++ b/tests/newline_fixtures.spec.ts @@ -1,39 +1,39 @@ /* - * edge-parser + * edge.js-parser * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import './assert-extend' +import './assert_extend.js' import { test } from '@japa/runner' -import { join } from 'path' -import { readdirSync, readFileSync, statSync } from 'fs' +import { fileURLToPath } from 'node:url' +import { dirname, join } from 'node:path' +import { readdirSync, readFileSync, statSync } from 'node:fs' -import * as tags from '../src/Tags' -import { Loader } from '../src/Loader' -import { Template } from '../src/Template' -import { Compiler } from '../src/Compiler' -import { Processor } from '../src/Processor' +import { Loader } from '../src/loader.js' +import * as tags from '../src/tags/main.js' +import { Template } from '../src/template.js' +import { Compiler } from '../src/compiler.js' +import { Processor } from '../src/processor.js' +import * as compatTags from '../src/migrate/tags/main.js' +import { normalizeNewLines, normalizeFilename } from '../tests_helpers/index.js' -import { normalizeNewLines, normalizeFilename } from '../test-helpers' - -const basePath = join(__dirname, '../newline-fixtures') +const basePath = join(dirname(fileURLToPath(import.meta.url)), '../newline_fixtures') const loader = new Loader() -loader.mount('default', basePath) - const processor = new Processor() -const compiler = new Compiler(loader, tags, processor) +loader.mount('default', basePath) test.group('Newline Fixtures', (group) => { group.setup(() => { Object.keys(tags).forEach((tag) => { - if (tags[tag].boot) { - tags[tag].boot(Template) - } + tags[tag as keyof typeof tags].boot?.(Template) + }) + Object.keys(compatTags).forEach((tag) => { + compatTags[tag as keyof typeof compatTags].boot?.(Template) }) }) @@ -41,7 +41,18 @@ test.group('Newline Fixtures', (group) => { dirs.forEach((dir) => { const dirBasePath = join(basePath, dir) + const compatMode = dir.endsWith('-compat') + test(dir, ({ assert }) => { + const compiler = new Compiler( + loader, + { + ...tags, + ...compatTags, + }, + processor, + { compat: compatMode } + ) const template = new Template(compiler, {}, {}, processor) /** @@ -58,6 +69,6 @@ test.group('Newline Fixtures', (group) => { .map((line) => normalizeFilename(dirBasePath, line)) .join('\n') ) - }) + }).tags(compatMode ? ['compat'] : []) }) }) diff --git a/tests/props.spec.ts b/tests/props.spec.ts new file mode 100644 index 0000000..4913732 --- /dev/null +++ b/tests/props.spec.ts @@ -0,0 +1,194 @@ +/* + * edge.js + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { test } from '@japa/runner' +import { ComponentProps } from '../src/component/props.js' + +test.group('ComponentProps', () => { + test('get all props', ({ assert }) => { + const props = ComponentProps.create({ title: 'Hello' }) + assert.deepEqual(props.all(), { title: 'Hello' }) + }) + + test('get value for a given key', ({ assert }) => { + const props = ComponentProps.create({ title: 'Hello' }) + assert.equal(props.get('title'), 'Hello') + assert.equal(props.title, 'Hello') + }) + + test('cherry pick values from the props', ({ assert }) => { + const props = ComponentProps.create({ + title: 'Hello', + label: 'Hello world', + actionText: 'Confirm', + }) + + assert.deepEqual(props.only(['label', 'actionText']).all(), { + label: 'Hello world', + actionText: 'Confirm', + }) + }) + + test('get values except for the defined keys from the props', ({ assert }) => { + const props = ComponentProps.create({ + title: 'Hello', + label: 'Hello world', + actionText: 'Confirm', + }) + + assert.deepEqual(props.except(['label', 'actionText']).all(), { + title: 'Hello', + }) + }) + + test('serialize props to html attributes', ({ assert }) => { + const props = ComponentProps.create({ + class: ['foo', 'bar'], + onclick: 'foo = bar', + }) + assert.equal(props.toAttrs().value, 'class="foo bar" onclick="foo = bar"') + }) + + test('serialize by merging custom properties', ({ assert }) => { + const props = ComponentProps.create({ + class: ['foo', 'bar'], + onclick: 'foo = bar', + }) + assert.equal( + props.merge({ id: '1' }).toAttrs().value, + 'id="1" class="foo bar" onclick="foo = bar"' + ) + }) + + test('serialize specific keys to html attributes', ({ assert }) => { + const props = ComponentProps.create({ + class: ['foo', 'bar'], + onclick: 'foo = bar', + }) + assert.equal(props.only(['class']).toAttrs().value, 'class="foo bar"') + }) + + test('serialize specific keys and merge custom properties', ({ assert }) => { + const props = ComponentProps.create({ + class: ['foo', 'bar'], + onclick: 'foo = bar', + }) + assert.equal(props.only(['class']).merge({ id: '1' }).toAttrs().value, 'id="1" class="foo bar"') + }) + + test('serialize all except defined keys to html attributes', ({ assert }) => { + const props = ComponentProps.create({ + class: ['foo', 'bar'], + onclick: 'foo = bar', + }) + + assert.equal(props.except(['class']).toAttrs().value, 'onclick="foo = bar"') + }) + + test('serialize specific keys and merge custom properties', ({ assert }) => { + const props = ComponentProps.create({ + class: ['foo', 'bar'], + onclick: 'foo = bar', + }) + + assert.equal( + props.except(['class']).merge({ id: '1' }).toAttrs().value, + 'id="1" onclick="foo = bar"' + ) + }) + + test('merge default and user supplied classes', ({ assert }) => { + const props = ComponentProps.create({ + class: ['foo', 'bar'], + onclick: 'foo = bar', + }) + + assert.equal( + props + .except(['onclick']) + .merge({ class: ['foo', 'baz'] }) + .toAttrs().value, + 'class="foo baz bar"' + ) + }) + + test('merge default and user supplied classes as object', ({ assert }) => { + const props = ComponentProps.create({ + class: [ + 'foo', + { + 'input-error': false, + 'input-disabled': true, + 'input-large': false, + 'input-medium': true, + 'input-rounded': true, + }, + ], + onclick: 'foo = bar', + }) + + assert.equal( + props + .except(['onclick']) + .merge({ class: ['foo', 'input-error'] }) + .toAttrs().value, + 'class="foo input-error input-disabled input-medium input-rounded"' + ) + }) + + test('mergeUnless a conditional is true', ({ assert }) => { + const props = ComponentProps.create({ + class: [ + 'foo', + { + 'input-error': false, + 'input-disabled': true, + 'input-large': false, + 'input-medium': true, + 'input-rounded': true, + }, + ], + ignoreExistingClasses: true, + onclick: 'foo = bar', + }) + + assert.equal( + props + .except(['onclick', 'ignoreExistingClasses']) + .mergeUnless(props.get('ignoreExistingClasses'), { class: ['foo', 'input-error'] }) + .toAttrs().value, + 'class="foo input-disabled input-medium input-rounded"' + ) + }) + + test('mergeIf a conditional is true', ({ assert }) => { + const props = ComponentProps.create({ + class: [ + 'foo', + { + 'input-error': false, + 'input-disabled': true, + 'input-large': false, + 'input-medium': true, + 'input-rounded': true, + }, + ], + applyDefaults: true, + onclick: 'foo = bar', + }) + + assert.equal( + props + .except(['onclick', 'applyDefaults']) + .mergeIf(props.get('applyDefaults'), { class: ['foo', 'input-error'] }) + .toAttrs().value, + 'class="foo input-error input-disabled input-medium input-rounded"' + ) + }) +}) diff --git a/test/props.spec.ts b/tests/props_compat.spec.ts similarity index 95% rename from test/props.spec.ts rename to tests/props_compat.spec.ts index 16a9092..35704d1 100644 --- a/test/props.spec.ts +++ b/tests/props_compat.spec.ts @@ -1,16 +1,18 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ import { test } from '@japa/runner' -import { Props } from '../src/Component/Props' +import { Props } from '../src/migrate/props.js' + +test.group('Props | Compat', (group) => { + group.tap((t) => t.tags(['compat'])) -test.group('Props', () => { test('get all props', ({ assert }) => { const props = new Props({ title: 'Hello' }) assert.deepEqual(props.all(), { title: 'Hello' }) @@ -94,7 +96,9 @@ test.group('Props', () => { onclick: 'foo = bar', }) + // @ts-expect-error assert.deepEqual(props['class'], ['foo', 'bar']) + // @ts-expect-error assert.deepEqual(props['onclick'], 'foo = bar') }) @@ -105,6 +109,7 @@ test.group('Props', () => { }, }) + // @ts-expect-error assert.equal(props['user']['name'], 'virk') }) diff --git a/tests/stringified_object.spec.ts b/tests/stringified_object.spec.ts new file mode 100644 index 0000000..279489e --- /dev/null +++ b/tests/stringified_object.spec.ts @@ -0,0 +1,43 @@ +/* + * edge.js + * + * (c) EdgeJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { test } from '@japa/runner' +import { StringifiedObject } from '../src/utils.js' + +test.group('StringifiedObject', () => { + test('add string as a key-value pair to object', ({ assert }) => { + const stringified = new StringifiedObject() + stringified.add('username', "'virk'") + assert.equal(stringified.flush(), "{ username: 'virk' }") + }) + + test('add number as a key-value pair to object', ({ assert }) => { + const stringified = new StringifiedObject() + stringified.add('username', '22') + assert.equal(stringified.flush(), '{ username: 22 }') + }) + + test('add boolean as a key-value pair to object', ({ assert }) => { + const stringified = new StringifiedObject() + stringified.add('username', 'true') + assert.equal(stringified.flush(), '{ username: true }') + }) + + test('add object as a key-value pair to object', ({ assert }) => { + const stringified = new StringifiedObject() + stringified.add('username', '{ age: 22 }') + assert.equal(stringified.flush(), '{ username: { age: 22 } }') + }) + + test('add array as a key-value pair to object', ({ assert }) => { + const stringified = new StringifiedObject() + stringified.add('username', '[10, 20]') + assert.equal(stringified.flush(), '{ username: [10, 20] }') + }) +}) diff --git a/test/tags.spec.ts b/tests/tags.spec.ts similarity index 93% rename from test/tags.spec.ts rename to tests/tags.spec.ts index 276e867..aea5ef2 100644 --- a/test/tags.spec.ts +++ b/tests/tags.spec.ts @@ -1,23 +1,25 @@ /* - * edge-parser + * edge.js-parser * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { test } from '@japa/runner' -import { join } from 'path' import dedent from 'dedent-js' +import { test } from '@japa/runner' +import { fileURLToPath } from 'node:url' +import { dirname, join } from 'node:path' import { Filesystem } from '@poppinss/dev-utils' -import * as tags from '../src/Tags' -import { Loader } from '../src/Loader' -import { Compiler } from '../src/Compiler' -import { Processor } from '../src/Processor' +import { Loader } from '../src/loader.js' +import * as tags from '../src/tags/main.js' +import { Compiler } from '../src/compiler.js' +import { Processor } from '../src/processor.js' +import * as compatTags from '../src/migrate/tags/main.js' -const fs = new Filesystem(join(__dirname, 'views')) +const fs = new Filesystem(join(dirname(fileURLToPath(import.meta.url)), 'views')) const loader = new Loader() loader.mount('default', fs.basePath) @@ -309,10 +311,11 @@ test.group('Component', (group) => { }) }) -test.group('Layouts', (group) => { +test.group('Layouts | Compat', (group) => { group.each.teardown(async () => { await fs.cleanup() }) + group.tap((t) => t.tags(['compat'])) test('raise error when section is nested inside conditional block', async ({ assert }) => { assert.plan(2) @@ -329,7 +332,7 @@ test.group('Layouts', (group) => { await fs.add('master.edge', "@!section('body')") try { - compiler.compile('foo') + new Compiler(loader, { ...tags, ...compatTags }, processor, { compat: true }).compile('foo') } catch (error) { assert.equal( error.stack.split('\n')[1], @@ -360,7 +363,7 @@ test.group('Layouts', (group) => { await fs.add('super.edge', "@!section('body')") try { - compiler.compile('foo') + new Compiler(loader, { ...tags, ...compatTags }, processor, { compat: true }).compile('foo') } catch (error) { assert.equal( error.stack.split('\n')[1], @@ -383,7 +386,7 @@ test.group('Layouts', (group) => { await fs.add('master.edge', "@!section('body')") try { - compiler.compile('foo') + new Compiler(loader, { ...tags, ...compatTags }, processor, { compat: true }).compile('foo') } catch (error) { assert.equal( error.stack.split('\n')[1], diff --git a/test/template.spec.ts b/tests/template.spec.ts similarity index 80% rename from test/template.spec.ts rename to tests/template.spec.ts index f537567..98b7610 100644 --- a/test/template.spec.ts +++ b/tests/template.spec.ts @@ -1,28 +1,28 @@ /* - * edge + * edge.js * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +import './assert_extend.js' import { test } from '@japa/runner' -import { join } from 'path' +import path, { join } from 'node:path' +import { fileURLToPath } from 'node:url' import { Filesystem } from '@poppinss/dev-utils' -import { Loader } from '../src/Loader' -import { Compiler } from '../src/Compiler' -import { slotTag } from '../src/Tags/Slot' -import { Processor } from '../src/Processor' -import { includeTag } from '../src/Tags/Include' -import { componentTag } from '../src/Tags/Component' -import { Template, safeValue } from '../src/Template' - -import './assert-extend' +import { Loader } from '../src/loader.js' +import { Compiler } from '../src/compiler.js' +import { slotTag } from '../src/tags/slot.js' +import { Processor } from '../src/processor.js' +import { includeTag } from '../src/tags/include.js' +import { componentTag } from '../src/tags/component.js' +import { Template, htmlSafe } from '../src/template.js' const tags = { slot: slotTag, component: componentTag, include: includeTag } -const fs = new Filesystem(join(__dirname, 'views')) +const fs = new Filesystem(join(path.dirname(fileURLToPath(import.meta.url)), 'views')) const loader = new Loader() loader.mount('default', fs.basePath) @@ -30,16 +30,19 @@ loader.mount('default', fs.basePath) test.group('Template', (group) => { group.each.teardown(async () => { await fs.cleanup() - Template.hydrate() }) - test('run template using the given state', async ({ assert }) => { + test('render template using the given state', async ({ assert }) => { await fs.add('foo.edge', 'Hello {{ username }}') + const processor = new Processor() const compiler = new Compiler(loader, tags, processor, { cache: false }) - const output = new Template(compiler, {}, {}, processor).render('foo', { + const template = new Template(compiler, {}, {}, processor) + + const output = template.render('foo', { username: 'virk', - }) as string + }) + assert.equal(output.trim(), 'Hello virk') }) @@ -47,8 +50,7 @@ test.group('Template', (group) => { await fs.add('foo.edge', 'Hello {{ getUsername() }}') const processor = new Processor() const compiler = new Compiler(loader, tags, processor, { cache: false }) - - const output = new Template( + const template = new Template( compiler, { username: 'virk' }, { @@ -57,18 +59,21 @@ test.group('Template', (group) => { }, }, processor - ).render('foo', {}) as string + ) + + const output = template.render('foo', {}) assert.equal(output.trim(), 'Hello VIRK') }) - test('run partial inside existing state', async ({ assert }) => { + test('compile and render a partial', async ({ assert }) => { await fs.add('foo.edge', 'Hello {{ username }}') const processor = new Processor() const compiler = new Compiler(loader, tags, processor, { cache: false }) const template = new Template(compiler, {}, {}, processor) + const partial = template.compilePartial('foo') - const output = template.compilePartial('foo')(template, { username: 'virk' }) + const output = partial(template, { username: 'virk' }, {}) assert.equal(output.trim(), 'Hello virk') }) @@ -78,9 +83,10 @@ test.group('Template', (group) => { const processor = new Processor() const compiler = new Compiler(loader, tags, processor, { cache: false }) const template = new Template(compiler, {}, {}, processor) + const partial = template.compilePartial('foo', 'user') const user = { username: 'virk' } - const output = template.compilePartial('foo', 'user')(template, {}, {}, user) + const output = partial(template, {}, {}, user) assert.equal(output.trim(), 'Hello virk') }) @@ -181,7 +187,7 @@ test.group('Template', (group) => { const processor = new Processor() const compiler = new Compiler(loader, tags, processor, { cache: false }) const template = new Template(compiler, {}, {}, processor) - assert.equal(template.escape(safeValue('

Hello world

')), '

Hello world

') + assert.equal(template.escape(htmlSafe('

Hello world

')), '

Hello world

') }) test('stringify array before escape', ({ assert }) => { @@ -206,24 +212,26 @@ test.group('Template', (group) => { }) test('add macros to context', ({ assert }) => { - Template.macro('upper', (username) => { + Template.macro('upper' as any, (username: string) => { return username.toUpperCase() }) const processor = new Processor() const compiler = new Compiler(loader, tags, processor, { cache: false }) const template = new Template(compiler, {}, {}, processor) + // @ts-expect-error assert.equal(template['upper']('virk'), 'VIRK') }) test('add getters to context', ({ assert }) => { - Template.getter('username', function username() { + Template.getter('username' as any, function username() { return 'virk' }) const processor = new Processor() const compiler = new Compiler(loader, tags, processor, { cache: false }) const template = new Template(compiler, {}, {}, processor) + // @ts-expect-error assert.equal(template['username'], 'virk') }) @@ -231,18 +239,13 @@ test.group('Template', (group) => { await fs.add('foo.edge', 'Hello {{ username }}') const processor = new Processor() - processor.process('compiled', ({ compiled }) => { - compiled = `username = 'virk'; \n ${compiled}` - return compiled - }) - const compiler = new Compiler(loader, tags, processor, { cache: true }) const template = new Template(compiler, {}, {}, processor) - assert.equal(template.compilePartial('foo')(template, {}).trim(), 'Hello undefined') - assert.equal( - template.compilePartial('foo', 'username')(template, 'username').trim(), - 'Hello virk' - ) + const partail = template.compilePartial('foo') + assert.equal(partail(template, {}, {}).trim(), 'Hello undefined') + + const partailWithInlineVariables = template.compilePartial('foo', 'username') + assert.equal(partailWithInlineVariables(template, {}, {}, 'virk').trim(), 'Hello virk') }) }) diff --git a/test-helpers/index.ts b/tests_helpers/index.ts similarity index 79% rename from test-helpers/index.ts rename to tests_helpers/index.ts index 75d2504..ad899cf 100644 --- a/test-helpers/index.ts +++ b/tests_helpers/index.ts @@ -1,16 +1,19 @@ /* - * edge-parser + * edge.js-parser * - * (c) Harminder Virk + * (c) EdgeJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { sep } from 'path' -import { EOL } from 'os' +import { sep } from 'node:path' +import { EOL } from 'node:os' +// @ts-ignore untyped module import stringify from 'js-stringify' +export const BASE_URL = new URL('./tmp/', import.meta.url) + export function normalizeNewLines(value: string) { // eslint-disable-next-line @typescript-eslint/quotes return value.replace(/\+=\s"\\n"/g, `+= ${EOL === '\n' ? `"\\n"` : `"\\r\\n"`}`) diff --git a/tsconfig.json b/tsconfig.json index 31496d8..2039043 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { - "extends": "./node_modules/@adonisjs/mrm-preset/_tsconfig", + "extends": "@adonisjs/tsconfig/tsconfig.package.json", "compilerOptions": { - "skipLibCheck": true + "rootDir": "./", + "outDir": "./build" } -} +} \ No newline at end of file diff --git a/typedoc.js b/typedoc.js deleted file mode 100644 index 740bfe9..0000000 --- a/typedoc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = require('@adonisjs/mrm-preset/_typedoc.js')({ - exclude: ['**/test/*.ts', '**/src/Tags/index.ts'], -})