--------------------------------------------------------------------------------
/generators/app/templates/repo.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%- desc %>
4 | <%- author %>
5 | <%- website %>
6 | <%- status %>
7 | <%- license %>
8 | true
9 | <%- apptype %>
10 | <%_ if (apptype !== 'library') { %><%- short %>
11 | <% } -%>
12 | <%_ if (pre) { %><%- prexq %>
13 | <% } -%>
14 | <%_ if (post) { %><%- postxq %>
15 | <% } -%>
16 | <%_ if (setperm) { %>
17 | <% } -%>
18 |
19 |
--------------------------------------------------------------------------------
/.github/no-response.yml:
--------------------------------------------------------------------------------
1 | # Configuration for probot-no-response - https://github.com/probot/no-response
2 |
3 | # Number of days of inactivity before an Issue is closed for lack of response
4 | daysUntilClose: 14
5 | # Label requiring a response
6 | responseRequiredLabel: more-information-needed
7 | # Comment to post when closing an Issue for lack of response. Set to `false` to disable
8 | closeComment: >
9 | This issue has been automatically closed because there has been no response
10 | to our request for more information from the original author. With only the
11 | information that is currently in the issue, we don't have enough information
12 | to take action. Please reach out if you have or find the answers we need so
13 | that we can investigate further.
14 |
--------------------------------------------------------------------------------
/generators/app/templates/specs/xqs/test-runner.xq:
--------------------------------------------------------------------------------
1 | xquery version "3.1";
2 |
3 | (:~ This library runs the XQSuite unit tests for the <%- title %> app.
4 | :
5 | : @author <%- author %>
6 | : @version <%- version %>
7 | : @see http://www.exist-db.org/exist/apps/doc/xqsuite
8 | :)
9 | import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
10 | import module namespace tests="<%- defuri %>/<%- defcoll %>/<%- short %>/tests" at "test-suite.xqm";
11 |
12 | declare namespace output="http://www.w3.org/2010/xslt-xquery-serialization";
13 | declare option output:method "json";
14 | declare option output:media-type "application/json";
15 |
16 | test:suite(
17 | inspect:module-functions(xs:anyURI("test-suite.xqm"))
18 | )
19 |
--------------------------------------------------------------------------------
/generators/app/templates/specs/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/e2e.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands'
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/generators/app/templates/pages/mysec/admin/security.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Generated page
7 |
8 |
9 |
This is a protected page. You must be logged in as a user with the appropriate
10 | privileges.
")
24 | function tests:templating-foo($n as xs:string) as node(){
25 | app:foo(element {$n} {}, $tests:map)
26 | };
27 | <% } else { %>
28 |
29 | declare
30 | %test:name('one-is-one')
31 | %test:assertTrue
32 | function tests:tautology() {
33 | 1 = 1
34 | };
35 | <% } -%>
36 |
--------------------------------------------------------------------------------
/test/fixtures/multi-suite.json:
--------------------------------------------------------------------------------
1 | {
2 | "testsuite": [
3 | {
4 | "package": "http://heml.mta.ca/Lace2/templates",
5 | "timestamp": "2020-10-13T12:19:13.927Z",
6 | "tests": "1",
7 | "failures": "0",
8 | "errors": "0",
9 | "pending": "0",
10 | "time": "PT0.013S",
11 | "testcase": {
12 | "name": "make img tag",
13 | "class": "app:getImageLink"
14 | }
15 | },
16 | {
17 | "package": "http://www.functx.com",
18 | "timestamp": "2020-10-13T12:19:13.932Z",
19 | "tests": "0",
20 | "failures": "0",
21 | "errors": "0",
22 | "pending": "0",
23 | "time": "PT0S"
24 | },
25 | {
26 | "package": "http://www.w3.org/2005/xquery-local-functions",
27 | "timestamp": "2020-10-13T12:19:13.933Z",
28 | "tests": "0",
29 | "failures": "0",
30 | "errors": "0",
31 | "pending": "0",
32 | "time": "PT0S"
33 | }
34 | ]
35 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Duncan Paterson (https://github.com/duncdrum)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/generators/app/templates/specs/mocha/rest_spec.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const supertest = require('supertest')
4 | const expect = require('chai').expect
5 |
6 | let client = supertest.agent('http://localhost:8080')
7 |
8 | describe('rest api returns', function () {
9 | it('404 from random page', function (done) {
10 | this.timeout(10000)
11 | client
12 | .get('/random')
13 | .expect(404)
14 | .end(function (err, res) {
15 | expect(res.status).to.equal(404)
16 | if (err) return done(err)
17 | done()
18 | })
19 | })
20 |
21 | it('200 from default rest endpoint', function (done) {
22 | client
23 | .get('/exist/rest/db/')
24 | .expect(200)
25 | .end(function (err, res) {
26 | expect(res.status).to.equal(200)
27 | if (err) return done(err)
28 | done()
29 | })
30 | })
31 |
32 | it<%_ if (apptype == 'empty') { %>.skip<% } _%>('file index.html exists in application root', function (done) {
33 | client
34 | .get('/exist/rest/db/<%- defcoll %>/<%- short %>/index.html')
35 | .expect(200)
36 | .end(function (err, res) {
37 | expect(res.status).to.equal(200)
38 | if (err) return done(err)
39 | done()
40 | })
41 | })
42 | })
--------------------------------------------------------------------------------
/generators/app/templates/xq/pre-install.xq:
--------------------------------------------------------------------------------
1 | xquery version "1.0";
2 | (:~ The pre-install runs before the actual install and deploy.
3 | :
4 | : @version <%- version %>
5 | :)
6 | import module namespace xdb="http://exist-db.org/xquery/xmldb";
7 |
8 | (: The following external variables are set by the repo:deploy function :)
9 |
10 | (: file path pointing to the exist installation directory :)
11 | declare variable $home external;
12 | (: path to the directory containing the unpacked .xar package :)
13 | declare variable $dir external;
14 | (: the target collection into which the app is deployed :)
15 | declare variable $target external;
16 |
17 | declare function local:mkcol-recursive($collection, $components) {
18 | if (exists($components)) then
19 | let $newColl := concat($collection, "/", $components[1])
20 | return (
21 | xdb:create-collection($collection, $components[1]),
22 | local:mkcol-recursive($newColl, subsequence($components, 2))
23 | )
24 | else
25 | ()
26 | };
27 |
28 | (: Helper function to recursively create a collection hierarchy. :)
29 | declare function local:mkcol($collection, $path) {
30 | local:mkcol-recursive($collection, tokenize($path, "/"))
31 | };
32 |
33 | (: store the collection configuration :)
34 | local:mkcol("/db/system/config", $target),
35 | xdb:store-files-from-pattern(concat("/db/system/config", $target), $dir, "*.xconf")
36 |
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on: [push, pull_request]
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | strategy:
12 | fail-fast: false
13 | matrix:
14 | node-version: ['18', '20']
15 | steps:
16 | - uses: actions/checkout@v4
17 | - name: Use Node.js ${{ matrix.node-version }}
18 | uses: actions/setup-node@v4
19 | with:
20 | node-version: ${{ matrix.node-version }}
21 | cache: 'npm'
22 | - run: npm ci
23 | - run: npm test
24 | continue-on-error: ${{ matrix.node-version == '18' }}
25 | release:
26 | name: Release
27 | runs-on: ubuntu-latest
28 | needs: build
29 | if: github.ref == 'refs/heads/main'
30 | steps:
31 | - name: Checkout
32 | uses: actions/checkout@v4
33 | with:
34 | fetch-depth: 0
35 | - name: Setup Node.js
36 | uses: actions/setup-node@v4
37 | with:
38 | node-version: 18
39 | cache: 'npm'
40 | - name: Install dependencies
41 | run: npm ci
42 | - name: Release
43 | env:
44 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
45 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
46 | run: npx semantic-release
47 |
48 |
49 |
--------------------------------------------------------------------------------
/generators/app/templates/pages/index.html:
--------------------------------------------------------------------------------
1 |
2 | <% if(apptype == 'plain'){ %>
3 |
4 |
5 |
6 | <% } else { %>
7 |
8 |
9 | <% } %>
10 |
Generated page
11 | <% if(apptype == 'plain'){ %>
12 |
<% } else { %>
13 |
<% } %>
14 |
15 |
16 | This is the entry page into your application and was generated by yeoman. It uses HTML templates for a clean separation of HTML views and application logic.
17 |
To add your own template functions, start by editing the XQuery module:
18 |
app.xqm
.
19 |
20 |
21 |
22 |
The page template uses the Bootstrap CSS library for the page layout.
23 |
24 |
25 |
26 |
27 |
28 |
29 | <% if(apptype == 'plain'){ %>
30 |
<% } else { %>
<% } %>
31 |
Application Info
32 |
33 |
34 | <% if(apptype == 'plain'){ %>
35 |
<% } else { %><% } %>
36 |
37 |
--------------------------------------------------------------------------------
/test/mock_rest.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const supertest = require('supertest')
3 | const expect = require('chai').expect
4 |
5 | // The client listening to the mock rest server
6 | const client = supertest.agent('http://localhost:3000')
7 |
8 | // mock test report
9 |
10 | describe('mocking xqSuite rest responses', function () {
11 | before(function (done) {
12 | require('../server').StartServer()
13 | done()
14 | })
15 |
16 | describe('mock-exist returns', function () {
17 | it('404 from random page', function (done) {
18 | client
19 | .get('/random')
20 | .expect(404)
21 | .end(function (err, res) {
22 | if (err) return done(err)
23 | expect(res.status).to.equal(404)
24 | done()
25 | })
26 | })
27 |
28 | it('200 from default rest endpoint', function (done) {
29 | client
30 | .get('/exist/rest/db/')
31 | .expect(200)
32 | .end(function (err, res) {
33 | if (err) return done(err)
34 | expect(res.status).to.equal(200)
35 | done()
36 | })
37 | })
38 |
39 | it.skip('200 from startpage (index.html)', function (done) {
40 | client
41 | .get('/exist/rest/db/<%- defcoll %>/<%- short %>/index.html')
42 | .expect(200)
43 | .end(function (err, res) {
44 | expect(res.status).to.equal(200)
45 | if (err) return done(err)
46 | done()
47 | })
48 | })
49 | })
50 |
51 | // see http://www.marcusoft.net/2015/10/eaddrinuse-when-watching-tests-with-mocha-and-supertest.html
52 | after('shutdown mock server', function (done) {
53 | done()
54 | })
55 | })
56 |
--------------------------------------------------------------------------------
/generators/app/templates/github/contributing.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | :balloon: First off, thank you for considering contributing to <%- title %>. :balloon:
4 |
5 | All pull-requests are welcome. File a bug report, fix a typo, improve the documentation, or add a new feature. All are helpful, and make <%- title %> better.
6 |
7 | Please have a quick look at these guidelines to help both you and the developers to keep things moving smoothly.
8 |
9 | ## Basic Conduct
10 |
11 | * Please be respectful when discussing changes and issues. Consider that not everyone's first language is english.
12 | * Create an issue for any major changes and enhancements that you wish to make.
13 |
14 | If this is your first time contributing to a project you might want to take a look [here](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github).
15 |
16 | ## Getting started
17 | 1. Create your own fork of the code.
18 |
19 | 2. Do the changes in your fork.
20 | * Minor changes that don't impact code functionality (typos, inline comments, … ) can be done in the master branch.
21 | * Functional changes should be collected in their own branch.
22 |
23 | 3. If you like the change and think the project could use it:
24 | * Open a pull request referencing the issue describing the new feature in the comment section \([how-to](https://github.com/blog/1506-closing-issues-via-pull-requests)\).
25 |
26 | Once your pull request has been reviewed it will be merged. You can greatly help the reviewer by:
27 | * sticking to one feature per pull request.
28 | * use [meaningful](https://chris.beams.io/posts/git-commit/) commit messages inside your feature branch.
29 | * filling out the pull request & issue templates.
30 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | exports.StartServer = function () {
2 | const restify = require('restify')
3 |
4 | const body = {
5 | testsuite: {
6 | package: 'http://exist-db.org/apps/my-app/tests',
7 | timestamp: '2018-07-12T15:25:38.227Z',
8 | tests: '1',
9 | failures: '0',
10 | errors: '0',
11 | pending: '0',
12 | time: 'PT0.02S',
13 | testcase: [{
14 | name: 'templating-foo',
15 | class: 'tests:templating-foo'
16 | }]
17 | }
18 | }
19 |
20 | // this rest server mocks the reponses of exist-db's rest api for unit testing without a running eXist instance.
21 | const server = restify.createServer({
22 | // server.use(restify.plugins.bodyParser())
23 | // server.use(restify.plugins.authorizationParser());
24 | name: 'mock-exist',
25 | version: '1.0.0'
26 | })
27 |
28 | // can be reached at eXist api's URI
29 | function respond (req, res, next) {
30 | res.setHeader('content-type', 'application/xml')
31 | res.charSet('UTF-8')
32 | res.send('world')
33 | next()
34 | }
35 |
36 | // respond with the result of running XQsuite test
37 | function runs (req, res, next) {
38 | res.setHeader('content-type', 'application/json')
39 | res.charSet('utf-8')
40 | res.send(200, body)
41 | next()
42 | }
43 |
44 | server.get('/exist/rest/db/', respond)
45 | server.head('/exist/rest/db/', respond)
46 |
47 | server.get('/exist/rest/db/my-app/modules/test-runner.xq', runs)
48 | server.head('/exist/rest/db/my-app/modules/test-runner.xq', runs)
49 |
50 | // do not use 8080!
51 | server.listen(3000, function () {
52 | console.log('%s listening at %s', server.name, server.url)
53 | })
54 |
55 | // server.close()
56 | }
57 |
--------------------------------------------------------------------------------
/generators/app/templates/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%- desc %>
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | <%_ if (apptype !== 'empty') { %>
14 |
15 |
16 |
17 |
18 | <% } -%>
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/generators/app/templates/xq/view.xq:
--------------------------------------------------------------------------------
1 | (:~
2 | : This is the main XQuery which will (by default) be called by controller.xq
3 | : to process any URI ending with ".html". It receives the HTML from
4 | : the controller and passes it to the templating system.
5 | :
6 | : @version <%- version %>
7 | :)
8 | xquery version "3.1";
9 |
10 | import module namespace templates="http://exist-db.org/xquery/html-templating";
11 | import module namespace lib="http://exist-db.org/xquery/html-templating/lib";
12 |
13 | (:
14 | : The following modules provide functions which will be called by the
15 | : templating.
16 | :)
17 |
18 | import module namespace config="<%- defuri %>/<%- defcoll %>/<%- short %>/config" at "config.xqm";
19 | import module namespace app="<%- defuri %>/<%- defcoll %>/<%- short %>/templates" at "app.xqm";
20 |
21 |
22 | declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
23 |
24 | declare option output:method "html5";
25 | declare option output:media-type "text/html";
26 |
27 | let $config := map {
28 | $templates:CONFIG_APP_ROOT: $config:app-root,
29 | $templates:CONFIG_STOP_ON_ERROR: true()
30 | }
31 | (:
32 | : We have to provide a lookup function to templates:apply to help it
33 | : find functions in the imported application modules. The templates
34 | : module cannot see the application modules, but the inline function
35 | : below does see them.
36 | :)
37 | let $lookup := function ($functionName as xs:string, $arity as xs:integer) {
38 | try {
39 | function-lookup(xs:QName($functionName), $arity)
40 | } catch * {
41 | ()
42 | }
43 | }
44 | (:
45 | : The HTML is passed in the request from the controller.
46 | : Run it through the templating system and return the result.
47 | :)
48 | let $content := request:get-data()
49 | return
50 | templates:apply($content, $lookup, (), $config)
51 |
--------------------------------------------------------------------------------
/generators/app/templates/xq/controller.xq:
--------------------------------------------------------------------------------
1 | xquery version "3.1";
2 |
3 | (:~ The controller library contains URL routing functions.
4 | :
5 | : @see http://www.exist-db.org/exist/apps/doc/urlrewrite.xml
6 | :)
7 |
8 | <%_ if (mysec) { %>
9 | import module namespace login="http://exist-db.org/xquery/login" at "resource:org/exist/xquery/modules/persistentlogin/login.xql";
10 | <% } -%>
11 |
12 | declare variable $exist:path external;
13 | declare variable $exist:resource external;
14 | declare variable $exist:controller external;
15 | declare variable $exist:prefix external;
16 | declare variable $exist:root external;
17 |
18 | <%_ if (mysec) { %>
19 | declare variable $local:login_domain := "org.exist-db.mysec";
20 | declare variable $local:user := $local:login_domain || '.user';
21 |
22 | let $logout := request:get-parameter("logout", ())
23 | let $set-user := login:set-user($local:login_domain, (), false())
24 | return
25 | <% } %>
26 |
27 | if ($exist:path eq '') then
28 |
29 |
30 |
31 |
32 | else if ($exist:path eq "/") then
33 | (: forward root path to index.xql :)
34 |
35 |
36 |
37 |
38 | else if (ends-with($exist:resource, ".html")) then (
39 | (: the html page is run through view.xq to expand templates :)
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | )
49 | else
50 | (: everything else is passed through :)
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/generators/app/templates/pages/mysec/templates/login-panel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/test/generated-pkg/app-mysec.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const assert = require('yeoman-assert')
4 | const helpers = require('yeoman-test')
5 | const fs = require('fs-extra')
6 |
7 | describe('eXide style …', function () {
8 | before(async function () {
9 | this.timeout(10000)
10 | await helpers.run(path.join(__dirname, '../../generators/app'))
11 | .withPrompts({
12 | title: 'foo',
13 | author: 'tester',
14 | email: 'te@st.er',
15 | apptype: ['exist-design', 'application'],
16 | mysec: true,
17 | pre: true,
18 | post: false,
19 | license: ['MIT', 'MIT', 'https://opensource.org/licenses/MIT'],
20 | github: false,
21 | setperm: false,
22 | atom: false
23 | })
24 | assert.noFile('readme.md')
25 | })
26 |
27 | describe('secure exist design has …', function () {
28 | it('default files and restricted area', function (done) {
29 | assert.file(['admin/controller.xq', 'admin/index.html', 'templates/login-panel.html', 'pre-install.xq', 'test/cypress/e2e/login-ok.cy.js'])
30 | done()
31 | })
32 |
33 | it('login section on index.html', function (done) {
34 | assert.fileContent('templates/page.html', 'org.exist-db.mysec.user')
35 | done()
36 | })
37 |
38 | it('expanded title in repo.xml', function (done) {
39 | assert.fileContent('repo.xml', /foo<\/target>/)
40 | done()
41 | })
42 | })
43 |
44 | describe('markup files are well-formed', function () {
45 | return require('../util/app').checkWellFormed()
46 | })
47 |
48 | // !! this should stay in yo's generator test only !!
49 | // Different editors use different settings
50 | describe('xml looks good', function () {
51 | return require('../util/gulp-ews').prettyDataEWS()
52 | })
53 |
54 | describe('app meta-data', function () {
55 | return require('../util/consistency').isConsistent()
56 | })
57 |
58 | describe('test_suite has …', function () {
59 | return require('../util/meta-test').metaTest()
60 | })
61 |
62 | after('teardown', function (done) {
63 | fs.emptyDirSync(process.cwd())
64 | done()
65 | })
66 | })
67 |
--------------------------------------------------------------------------------
/test/util/app.js:
--------------------------------------------------------------------------------
1 | exports.checkWellFormed = function () {
2 | const chai = require('chai')
3 | const chaiXml = require('chai-xml')
4 | const expect = require('chai').expect
5 | const fs = require('fs-extra')
6 | const glob = require('glob')
7 | const xmldoc = require('xmldoc')
8 |
9 | chai.use(chaiXml)
10 | it('*.html is xhtml', function (done) {
11 | glob('**/*.html', { ignore: ['node_modules/**', 'bower_components/**'] }, function (err, files) {
12 | if (err) throw err
13 | // console.log(files)
14 | files.forEach(function (html) {
15 | const xhtml = fs.readFileSync(html, 'utf8')
16 | const hParsed = new xmldoc.XmlDocument(xhtml).toString()
17 | expect(hParsed).xml.to.be.valid()
18 | })
19 | })
20 | done()
21 | })
22 |
23 | it('*.xml', function (done) {
24 | glob('**/*.xml', { ignore: ['node_modules/**', 'bower_components/**'] }, function (err, files) {
25 | if (err) throw err
26 | // console.log(files)
27 | files.forEach(function (xmls) {
28 | const xml = fs.readFileSync(xmls, 'utf8')
29 | const xParsed = new xmldoc.XmlDocument(xml).toString()
30 | expect(xParsed).xml.to.be.valid()
31 | })
32 | })
33 | done()
34 | })
35 |
36 | it('*.xconf', function (done) {
37 | glob('**/*.xconf', { ignore: ['node_modules/**', 'bower_components/**'] }, function (err, files) {
38 | if (err) throw err
39 | // console.log(files)
40 | files.forEach(function (xconfs) {
41 | const xconf = fs.readFileSync(xconfs, 'utf8')
42 | const cParsed = new xmldoc.XmlDocument(xconf).toString()
43 | expect(cParsed).xml.to.be.valid()
44 | })
45 | })
46 | done()
47 | })
48 | it('*.odd', function (done) {
49 | this.slow(1000)
50 | glob('**/*.odd', { ignore: ['node_modules/**', 'bower_components/**'] }, function (err, files) {
51 | if (err) throw err
52 | // console.log(files)
53 | files.forEach(function (odds) {
54 | const odd = fs.readFileSync(odds, 'utf8')
55 | const xParsed = new xmldoc.XmlDocument(odd).toString()
56 | expect(xParsed).xml.to.be.valid()
57 | })
58 | })
59 | done()
60 | })
61 | }
62 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[BUG]"
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | :balloon: First off, thank you for contributing to eXist-db's yeoman generator. :balloon:
11 |
12 | **Describe the bug**
13 | > A clear and concise description of what the bug is.
14 |
15 | **Expected behavior**
16 | > A clear and concise description of what you expected to happen.
17 |
18 | **To Reproduce**
19 | > The *best* way is to provide an [SSCCE (Short, Self Contained, Correct (Compilable), Example)](http://sscce.org/). One type of SSCCE could be a small test which reproduces the issue and can be run without dependencies. Please locate the existing test-suite and follow the examples provided there:
20 |
21 | **Unit Test**
22 | The generator uses uses [mocha](https://mochajs.org) and the `yeoman-test` framework for for its unit and integration tests.
23 |
24 | You can find mock app configs inside `test/generated-pkg`. When a bug effects multiple app types, it is generally sufficient to add another test case for just one app type.
25 |
26 | ```javascript
27 | const assert = require('yeoman-assert')
28 | const helpers = require('yeoman-test')
29 | const fs = require('fs-extra')
30 |
31 | // this is a dummy test
32 | describe('exist design has …', function () {
33 | it('default files', function (done) {
34 | assert.file(['repo.xml', 'modules/app.xql'])
35 | done()
36 | })
37 |
38 | it('type specific files', function (done) {
39 | assert.file(['resources/css/exist-2.2.css'])
40 | done()
41 | })
42 | })
43 | ```
44 |
45 | If none of the above is working, please tell us the exact steps you took when you encountered the problem:
46 | 1. Go to '...'
47 | 2. Click on '....'
48 | 3. Scroll down to '....'
49 | 4. See error
50 |
51 | **Screenshots**
52 | If applicable, add screenshots to help explain your problem.
53 |
54 | **Logs, error messages**
55 | > Paste the contents of error messages or log files here (exist.log, stdout). What does `npm test` produce?
56 |
57 | **Context (please always complete the following information):**
58 | - node version: `x.x.x`
59 | - eXist-db version: `x.x.x`
60 | - generator version: `x.x.x`
61 | - install method: `npm / github`
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@existdb/generator-exist",
3 | "version": "1.0.0",
4 | "description": "exist-db app boilerplate",
5 | "homepage": "https://github.com/eXist-db/generator-exist#readme",
6 | "bugs": "https://github.com/eXist-db/generator-exist/issues",
7 | "engines": {
8 | "node": ">=14.0.0"
9 | },
10 | "author": {
11 | "name": "Duncan Paterson",
12 | "email": "d.paterson@me.com",
13 | "url": "https://github.com/duncdrum"
14 | },
15 | "publishConfig": {
16 | "access": "public"
17 | },
18 | "license": "MIT",
19 | "files": [
20 | "generators"
21 | ],
22 | "main": "generators/app/index.js",
23 | "keywords": [
24 | "exist",
25 | "xml",
26 | "xql",
27 | "xquery",
28 | "exist-db",
29 | "prototype",
30 | "yeoman-generator"
31 | ],
32 | "devDependencies": {
33 | "@babel/core": "^7.23.2",
34 | "chai": "^4.3.10",
35 | "chai-xml": "^0.4.1",
36 | "eslint": "^8.52.0",
37 | "eslint-config-standard": "^17.1.0",
38 | "eslint-config-xo-space": "^0.34.0",
39 | "eslint-plugin-import": "^2.29.0",
40 | "eslint-plugin-mocha": "^10.2.0",
41 | "eslint-plugin-n": "^16.2.0",
42 | "eslint-plugin-promise": "^6.1.1",
43 | "fs-extra": "^11.2.0",
44 | "glob": "^10.3.10",
45 | "mocha": "^10.2.0",
46 | "restify": "^11.1.0",
47 | "semantic-release": "^22.0.5",
48 | "stylelint": "^16.0.1",
49 | "stylelint-config-standard": "^35.0.0",
50 | "supertest": "^6.3.3",
51 | "xmldoc": "^1.3.0",
52 | "yeoman-assert": "^3.1.1",
53 | "yeoman-test": "^6.3.0"
54 | },
55 | "dependencies": {
56 | "chalk": "^4.1.2",
57 | "generator-license": "^5.6.0",
58 | "gulp-pretty-data": "^0.1.2",
59 | "gulp-stripbom": "^1.0.5",
60 | "yeoman-environment": "^3.19.3",
61 | "yeoman-generator": "^5.9.0",
62 | "yosay": "^2.0.2"
63 | },
64 | "scripts": {
65 | "test": "mocha --recursive --exit && eslint --fix generators/app/**/*.js test/*.js *.js && stylelint --fix 'generators/app/templates/styles/exist-2.2.css'"
66 | },
67 | "eslintConfig": {
68 | "plugins": [
69 | "mocha"
70 | ],
71 | "extends": "standard",
72 | "env": {
73 | "mocha": true,
74 | "node": true
75 | }
76 | },
77 | "release": {
78 | "branches": "main"
79 | },
80 | "repository": {
81 | "type": "git",
82 | "url": "https://github.com/eXist-db/generator-exist.git",
83 | "license": "MIT"
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/test/generated-pkg/app-empty.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const assert = require('yeoman-assert')
4 | const helpers = require('yeoman-test')
5 | const fs = require('fs-extra')
6 |
7 | describe('empty package', function () {
8 | this.timeout(15000)
9 | before(async function () {
10 | await helpers.run(path.join(__dirname, '../../generators/app'))
11 | .withPrompts({
12 | title: 'foo',
13 | author: 'tester',
14 | email: 'te@st.er',
15 | apptype: ['empty', 'application'],
16 | pre: false,
17 | post: false,
18 | license: ['MIT', 'MIT', 'https://opensource.org/licenses/MIT'],
19 | github: false,
20 | atom: false
21 | })
22 | assert.noFile(['modules/app.xqm', 'templates/page.html', 'test/cypress/e2e/login-ok.cy.js', 'index.html'])
23 | })
24 |
25 | describe('empty has', function () {
26 | it('recommended files', function (done) {
27 | assert.file(['repo.xml', 'test/mocha/app_spec.js', 'test/xqs/test-runner.xq'])
28 | done()
29 | })
30 |
31 | it('selected license', function (done) {
32 | assert.fileContent('LICENSE', 'MIT License')
33 | done()
34 | })
35 |
36 | it('a repo.xml with expanded target URL', function (done) {
37 | assert.fileContent('repo.xml', /foo<\/target>/)
38 | done()
39 | })
40 |
41 | it('no dependency on shared-resources or templating', function (done) {
42 | assert.noFileContent('expath-pkg.xml', 'http://exist-db.org/apps/shared')
43 | assert.noFileContent('expath-pkg.xml', 'http://exist-db.org/html-templating')
44 | done()
45 | })
46 |
47 | it('no default node dependencies', function (done) {
48 | assert.noFileContent('build.xml', 'bootstrap')
49 | done()
50 | })
51 |
52 | it('xqs does not import app module', function (done) {
53 | assert.noFileContent('test/xqs/test-runner.xq', 'import module namespace app')
54 | done()
55 | })
56 | })
57 |
58 | describe('markup files are well-formed', function () {
59 | return require('../util/app').checkWellFormed()
60 | })
61 |
62 | describe('app meta-data', function () {
63 | return require('../util/consistency').isConsistent()
64 | })
65 |
66 | describe('test_suite has …', function () {
67 | return require('../util/meta-test').metaTest()
68 | })
69 |
70 | after('teardown', function (done) {
71 | fs.emptyDirSync(process.cwd())
72 | done()
73 | })
74 | })
75 |
--------------------------------------------------------------------------------
/test/generated-pkg/app-eXide-plain.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const assert = require('yeoman-assert')
4 | const helpers = require('yeoman-test')
5 | const fs = require('fs-extra')
6 |
7 | describe('eXide plain app', function () {
8 | before(async function () {
9 | this.timeout(10000)
10 | await helpers.run(path.join(__dirname, '../../generators/app'))
11 | .withPrompts({
12 | title: 'foo',
13 | author: 'tester',
14 | email: 'te@st.er',
15 | apptype: ['plain', 'application'],
16 | pre: false,
17 | post: false,
18 | license: ['MIT', 'MIT', 'https://opensource.org/licenses/MIT'],
19 | setperm: false,
20 | github: false,
21 | ci: 'GitHub Action',
22 | docker: true,
23 | dockertag: '5.0.0',
24 | atom: true,
25 | instance: 'http://localhost:8080/exist',
26 | admin: 'admin',
27 | adminpw: 'pw123'
28 | })
29 | assert.noFile(['resources/images/bold.gif', 'pre-install.xq', 'test/cypress/e2e/secure.cy.js', '.travis.yml', 'resources/css/exist-2.2.css'])
30 | })
31 |
32 | describe('plain package has', function () {
33 | it('recommended files', function (done) {
34 | assert.file(['expath-pkg.xml', 'modules/config.xqm', 'test/xqs/test-runner.xq', 'controller.xq', '.dockerignore', 'resources/images/powered-by.svg'])
35 | done()
36 | })
37 |
38 | it('default node dependencies', function (done) {
39 | assert.fileContent('build.xml', 'bootstrap')
40 | done()
41 | })
42 |
43 | it('atom file with proper uri', function (done) {
44 | assert.fileContent('.existdb.json', 'http://localhost:8080/exist')
45 | assert.fileContent('.existdb.json', 'pw123')
46 | done()
47 | })
48 |
49 | it('dockerfile with expanded package name', function (done) {
50 | assert.fileContent('Dockerfile', 'foo-1.0.0.xar')
51 | done()
52 | })
53 |
54 | it('GitHub Action Workflow', function (done) {
55 | assert.fileContent('.github/workflows/exist.yml', 'cypress')
56 | done()
57 | })
58 |
59 | it('expanded title on index.html', function (done) {
60 | assert.fileContent('templates/page.html', 'foo')
61 | done()
62 | })
63 | })
64 | describe('markup files are well-formed', function () {
65 | return require('../util/app').checkWellFormed()
66 | })
67 |
68 | describe('app meta-data', function () {
69 | return require('../util/consistency').isConsistent()
70 | })
71 |
72 | describe('test_suite has …', function () {
73 | return require('../util/meta-test').metaTest()
74 | })
75 |
76 | after('teardown', function (done) {
77 | fs.emptyDirSync(process.cwd())
78 | done()
79 | })
80 | })
81 |
--------------------------------------------------------------------------------
/test/generated-pkg/app-lib.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const assert = require('yeoman-assert')
4 | const helpers = require('yeoman-test')
5 | const fs = require('fs-extra')
6 |
7 | describe('library package', function () {
8 | this.timeout(10000)
9 | before(async function () {
10 | await helpers.run(path.join(__dirname, '../../generators/app'))
11 | .withPrompts({
12 | title: 'foo',
13 | author: 'tester',
14 | email: 'te@st.er',
15 | apptype: ['empty', 'library'],
16 | pre: false,
17 | post: false,
18 | license: ['LGPL-3.0', 'LGPL%20v3', 'https://www.gnu.org/licenses/lgpl-3.0'],
19 | // TODO: #572
20 | github: true,
21 | atom: false,
22 | ci: 'travis'
23 | })
24 | assert.noFile(['modules/app.xqm', 'modules/test-suite.xqm', 'templates/page.html', 'reports/screenshots/.gitkeep', 'controller.xq', 'collection.xconf'])
25 | })
26 |
27 | describe('library has', function () {
28 | it('only recommended files', function (done) {
29 | assert.file(['repo.xml', 'README.md', '.git/config', '.gitignore', 'test/xqs/test-suite.xqm'])
30 | done()
31 | })
32 |
33 | it('no integration test on ci', function (done) {
34 | assert.noFileContent('.travis.yml', 'cypress')
35 | done()
36 | })
37 |
38 | it('selected license', function (done) {
39 | assert.fileContent('LICENSE', 'GNU LESSER GENERAL PUBLIC LICENSE')
40 | done()
41 | })
42 |
43 | it('no target URL in repo.xml', function (done) {
44 | assert.noFileContent('repo.xml', //)
45 | done()
46 | })
47 |
48 | it('no dependency on shared-resources or templating', function (done) {
49 | assert.noFileContent('expath-pkg.xml', 'http://exist-db.org/apps/shared')
50 | assert.noFileContent('expath-pkg.xml', 'http://exist-db.org/html-templating')
51 | done()
52 | })
53 |
54 | it('xqs does not call app module', function (done) {
55 | assert.noFileContent('test/xqs/test-suite.xqm', 'import module namespace app')
56 | done()
57 | })
58 |
59 | it('pkgJson with repo info', function (done) {
60 | assert.fileContent('package.json', 'git')
61 | done()
62 | })
63 |
64 | it('pkgJson without cypress script', function (done) {
65 | assert.noFileContent('package.json', 'cypress')
66 | done()
67 | })
68 | })
69 |
70 | describe('markup files are well-formed', function () {
71 | return require('../util/app').checkWellFormed()
72 | })
73 |
74 | describe('app meta-data', function () {
75 | return require('../util/consistency').isConsistent()
76 | })
77 |
78 | after('teardown', function (done) {
79 | fs.emptyDirSync(process.cwd())
80 | done()
81 | })
82 | })
83 |
--------------------------------------------------------------------------------
/test/generated-pkg/app-eXide-default.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const assert = require('yeoman-assert')
4 | const helpers = require('yeoman-test')
5 | const fs = require('fs-extra')
6 |
7 | describe('eXide style …', function () {
8 | before(async function () {
9 | this.timeout(20000)
10 | await helpers.run(path.join(__dirname, '../../generators/app'))
11 | .withPrompts({
12 | title: 'foo',
13 | author: 'tester',
14 | email: 'te@st.er',
15 | apptype: ['exist-design', 'application'],
16 | pre: true,
17 | post: true,
18 | license: ['MIT', 'MIT', 'https://opensource.org/licenses/MIT'],
19 | github: false,
20 | setperm: false,
21 | ci: 'travis',
22 | docker: false,
23 | atom: false
24 | })
25 | assert.noFile('readme.md', 'Dockerfile')
26 | })
27 |
28 | describe('exist design has …', function () {
29 | it('default files', function (done) {
30 | assert.file(['repo.xml', 'modules/app.xqm', 'post-install.xq', 'pre-install.xq', 'test/xqs/test-suite.xqm'])
31 | done()
32 | })
33 |
34 | it('type specific files', function (done) {
35 | assert.file(['resources/css/exist-2.2.css'])
36 | done()
37 | })
38 |
39 | it('declared dependency on templating library', function (done) {
40 | assert.fileContent('expath-pkg.xml', 'http://exist-db.org/html-templating')
41 | done()
42 | })
43 |
44 | it('expanded title on index.html', function (done) {
45 | assert.fileContent('templates/page.html', 'foo')
46 | done()
47 | })
48 |
49 | it('integration tests for travis', function (done) {
50 | assert.fileContent('.travis.yml', 'cypress')
51 | done()
52 | })
53 |
54 | it('expanded title in repo.xml', function (done) {
55 | assert.fileContent('repo.xml', /foo<\/target>/)
56 | done()
57 | })
58 | })
59 |
60 | describe('markup files are well-formed', function () {
61 | return require('../util/app').checkWellFormed()
62 | })
63 |
64 | // !! this should stay in yo's generator test only !!
65 | // Different editors use different settings
66 | describe('xml looks good', function () {
67 | return require('../util/gulp-ews').prettyDataEWS()
68 | })
69 |
70 | describe('app meta-data', function () {
71 | return require('../util/consistency').isConsistent()
72 | })
73 |
74 | describe('test_suite has …', function () {
75 | return require('../util/meta-test').metaTest()
76 | })
77 |
78 | // Checking Xquery files requires updates to xqlint
79 | // it('linted XQuery', function () {
80 | // let xq = fs.readFileSync('modules/app.xqm')
81 | // let xql = new xmldoc.XmlDocument(xq).toString()
82 | // expect(doc).xml.to.be.valid()
83 | // })
84 |
85 | after('teardown', function (done) {
86 | fs.emptyDirSync(process.cwd())
87 | done()
88 | })
89 | })
90 |
--------------------------------------------------------------------------------
/generators/app/templates/xq/app.xqm:
--------------------------------------------------------------------------------
1 | xquery version "3.1";
2 |
3 | (:~ This is the default application library module of the <%- title %> app.
4 | :
5 | : @author <%- author %>
6 | : @version <%- version %>
7 | : @see <%- website %>
8 | :)
9 |
10 | (: Module for app-specific template functions :)
11 | module namespace app="<%- defuri %>/<%- defcoll %>/<%- short %>/templates";
12 | import module namespace templates="http://exist-db.org/xquery/html-templating";
13 | import module namespace lib="http://exist-db.org/xquery/html-templating/lib";
14 | import module namespace config="<%- defuri %>/<%- defcoll %>/<%- short %>/config" at "config.xqm";
15 |
16 |
17 | (:~
18 | : This is a sample templating function. It will be called by the templating module if
19 | : it encounters an HTML element with an attribute: data-template="app:test" or class="app:test" (deprecated).
20 | : The function has to take 2 default parameters. Additional parameters are automatically mapped to
21 | : any matching request or function parameter.
22 | :
23 | : @param $node the HTML node with the attribute which triggered this call
24 | : @param $model a map containing arbitrary data - used to pass information between template calls
25 | :)
26 |
27 | declare
28 | %templates:wrap
29 | function app:foo($node as node(), $model as map(*)) {
30 |
Dummy templating function.
31 | };
32 |
33 | declare function app:test($node as node(), $model as map(*)) {
34 |
Dummy template output generated by function app:test at {current-dateTime()}. The templating
35 | function was triggered by the class attribute class="app:test".