@open-xchange/vite-plugin-ox-manifests 0.2.0-pre8 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.gitlab-ci.yml CHANGED
@@ -4,6 +4,9 @@ stages:
4
4
  - test
5
5
 
6
6
  unit tests:
7
+ variables:
8
+ KUBERNETES_CPU_REQUEST: 2
9
+ KUBERNETES_CPU_LIMIT: 3
7
10
  stage: test
8
11
  script:
9
12
  - apk add git
package/index.js CHANGED
@@ -6,6 +6,7 @@ import servePlugin from './src/plugins/serve.js'
6
6
  import serverManifestPlugin from './src/plugins/server-manifests.js'
7
7
  import gettextPlugin from './src/plugins/gettext.js'
8
8
  import { deepMergeObject } from './src/util.js'
9
+ import additionalManifestsPlugin from './src/plugins/additional-manifests.js'
9
10
 
10
11
  /**
11
12
  * Creates a vite-plugin to include manifests in dev and production mode
@@ -13,7 +14,7 @@ import { deepMergeObject } from './src/util.js'
13
14
  * @param {object} [options]
14
15
  * @param {boolean} [options.watch=false] - If set to true, this plugin will watch manifest changes and reload the ui
15
16
  * @param {string} [options.appsuiteUrl] - When used in dev-mode, the dev-server can merge the local manifests with the manifest-files from the appsuiteUrl server.
16
- * @param {string} [options.manifestApiPathPath='/api/manifest.json'] - The path at appsuiteUrl for manifest files. I omitted, no manifests from a server will be used.
17
+ * @param {Array} [options.additionalManifestUrls] - You can add additional urls which can point to manifest files during development. This parameter will be ignored in production
17
18
  * @param {string} [options.entryPoints] - Convenience method to specify additional entry points for the production build. Can be specified as a glob-pattern.
18
19
  * @param {boolean} [options.manifestsAsEntryPoints=true] - Specifies, that every path in a manifest is used as an entry-point for the production build.
19
20
  * @param {boolean} [options.transformAbsolutePaths=true] - If this is set to true, every path in the html-files is transformed to a relative path. Additionally, every preloaded file will also be loaded relative to the index.html
@@ -23,8 +24,8 @@ import { deepMergeObject } from './src/util.js'
23
24
  export default function ({
24
25
  watch = false,
25
26
  appsuiteUrl,
27
+ additionalManifestUrls,
26
28
  entryPoints,
27
- manifestApiPath = '/api/manifest.json',
28
29
  manifestsAsEntryPoints = true,
29
30
  transformAbsolutePaths = true,
30
31
  autoloadSettings = true,
@@ -33,8 +34,8 @@ export default function ({
33
34
  const options = {
34
35
  watch,
35
36
  appsuiteUrl,
37
+ additionalManifestUrls,
36
38
  entryPoints,
37
- manifestApiPath,
38
39
  manifestsAsEntryPoints,
39
40
  transformAbsolutePaths,
40
41
  autoloadSettings,
@@ -47,6 +48,7 @@ export default function ({
47
48
  relativePathsPlugin(options),
48
49
  servePlugin(options),
49
50
  serverManifestPlugin(options),
51
+ additionalManifestsPlugin(options),
50
52
  gettextPlugin(options),
51
53
  // manifest plugin last
52
54
  manifestsPlugin(options)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-xchange/vite-plugin-ox-manifests",
3
- "version": "0.2.0-pre8",
3
+ "version": "0.2.2",
4
4
  "description": "A vite plugin to concat and serve ox manifests",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -35,7 +35,7 @@
35
35
  "less": "^4.1.1",
36
36
  "rollup": "^2.40.0",
37
37
  "rollup-plugin-auto-external": "^2.0.0",
38
- "vite": "^2.4.2"
38
+ "vite": "^2.5.2"
39
39
  },
40
40
  "lint-staged": {
41
41
  "*.js": "eslint --cache --fix"
@@ -0,0 +1,18 @@
1
+ import axios from 'axios'
2
+
3
+ export default function ({ additionalManifestUrls = [] } = {}) {
4
+ return {
5
+ async getManifests () {
6
+ const manifestSets = await Promise.all(additionalManifestUrls.map(async url => {
7
+ try {
8
+ const res = await axios.get(url)
9
+ return res.data
10
+ } catch (e) {
11
+ console.error(`Could not fetch manifest ${url}`, e)
12
+ return []
13
+ }
14
+ }))
15
+ return manifestSets.flat(1)
16
+ }
17
+ }
18
+ }
@@ -23,19 +23,6 @@ async function getManifestEntryFile (basePath, extensions) {
23
23
  throw new Error(`Cannot find file "${basePath}"`)
24
24
  }
25
25
 
26
- const getViteManifestFor = (() => {
27
- let cache
28
- return (id, viteManifests, supportedEntryExtensions) => {
29
- if (!cache) {
30
- cache = {}
31
- Object.values(viteManifests).forEach(manifest => {
32
- cache[manifest.file] = manifest
33
- })
34
- }
35
- return supportedEntryExtensions.reduce((memo, ext) => memo || cache[`${id}.${ext}`], null)
36
- }
37
- })()
38
-
39
26
  export default function ({ supportedEntryExtensions, entryPoints, manifestsAsEntryPoints } = {}) {
40
27
  const manifestModules = {}
41
28
  let resolvedConfig
@@ -131,44 +118,17 @@ export default function ({ supportedEntryExtensions, entryPoints, manifestsAsEnt
131
118
 
132
119
  async generateBundle (options, bundle) {
133
120
  const viteManifests = await viteManifestPlugin.getManifests(options, bundle)
134
- const oxManifests = []
135
121
 
136
- for (const file in bundle) {
137
- const chunk = bundle[file]
122
+ for (const entry in viteManifests) {
123
+ const chunk = bundle[viteManifests[entry].file]
138
124
  if (!chunk.facadeModuleId) continue
139
- if (chunk.isDynamicEntry) {
140
- const path = basepath(chunk.fileName).replace(/\\/g, '/')
141
- const manifest = {
142
- namespace: 'dynamicEntry',
143
- path
144
- }
145
- const manifestInfo = getViteManifestFor(manifest.path, viteManifests, supportedEntryExtensions)
146
- const dependencies = [].concat(manifestInfo.imports || []).concat(manifestInfo.css || []).map(i => {
147
- if (viteManifests[i] && viteManifests[i].file) return viteManifests[i].file
148
- return i
149
- }).filter(Boolean)
150
- if (dependencies.length) manifest.dependencies = dependencies
151
- oxManifests.push(manifest)
152
- }
153
- const meta = this.getModuleInfo(chunk.facadeModuleId).meta
154
- if (!meta?.manifests) continue
155
-
156
- meta.manifests.forEach((data) => {
157
- const manifest = { ...data, path: basepath(chunk.fileName).replace(/\\/g, '/') }
158
- const manifestInfo = getViteManifestFor(manifest.path, viteManifests, supportedEntryExtensions)
159
- const dependencies = [].concat(manifestInfo.imports || []).concat(manifestInfo.css || []).map(i => {
160
- if (viteManifests[i] && viteManifests[i].file) return viteManifests[i].file
161
- return i
162
- }).filter(Boolean)
163
- if (dependencies.length) manifest.dependencies = dependencies
164
- oxManifests.push(manifest)
165
- })
125
+ viteManifests[entry].meta = this.getModuleInfo(chunk.facadeModuleId).meta
166
126
  }
167
127
 
168
128
  this.emitFile({
169
129
  fileName: 'manifest.json',
170
130
  type: 'asset',
171
- source: JSON.stringify(oxManifests)
131
+ source: JSON.stringify(viteManifests)
172
132
  })
173
133
  }
174
134
  }
@@ -5,13 +5,18 @@ export default function ({ transformAbsolutePaths } = {}) {
5
5
  return {
6
6
  transform (code, id) {
7
7
  if (id === 'vite/preload-helper') {
8
- const mapCode = 'return Promise.all(deps.map((dep) => {'
9
- if (code.indexOf(mapCode) < 0) {
8
+ // 2.5.2 transformation
9
+ if (code.search(/const base = '[^']*'/) >= 0) {
10
+ const s = new MagicString(code.replace(/const base = '[^']*'/, 'const base = \'./\''))
11
+ return { code: s.toString(), map: s.generateMap({ hires: true }) }
12
+ } else if (code.indexOf('return Promise.all(deps.map((dep) => {')) {
13
+ // pre 2.5 transformation
14
+ const code = 'return Promise.all(deps.map((dep) => {'
15
+ const s = new MagicString(code.replace(code, `${code}\ndep = dep.startsWith('/') ? \`.\${dep}\` : \`./\${dep}\``))
16
+ return { code: s.toString(), map: s.generateMap({ hires: true }) }
17
+ } else {
10
18
  throw new Error(`Cannot transform preload helper. Consider upgrading "${name}".`)
11
19
  }
12
-
13
- const s = new MagicString(code.replace(mapCode, `${mapCode}\ndep = dep.startsWith('/') ? \`.\${dep}\` : \`./\${dep}\``))
14
- return { code: s.toString(), map: s.generateMap({ hires: true }) }
15
20
  }
16
21
  },
17
22
 
@@ -2,7 +2,14 @@ import path from 'path'
2
2
  import parseurl from 'parseurl'
3
3
  import chokidar from 'chokidar'
4
4
 
5
- export default function ({ watch, manifestApiPath } = {}) {
5
+ const sendJSON = (res, data) => {
6
+ res.statusCode = 200
7
+ res.setHeader('Content-Type', 'application/json')
8
+ res.write(JSON.stringify(data))
9
+ return res.end()
10
+ }
11
+
12
+ export default function ({ watch } = {}) {
6
13
  let resolvedConfig
7
14
 
8
15
  return {
@@ -25,14 +32,18 @@ export default function ({ watch, manifestApiPath } = {}) {
25
32
 
26
33
  middlewares.use(async (req, res, next) => {
27
34
  const { path } = parseurl(req)
28
- if (path === manifestApiPath && req.method === 'GET') {
29
- res.statusCode = 200
30
- res.setHeader('Content-Type', 'application/json')
35
+ if (req.method !== 'GET') return next()
36
+
37
+ if (path === '/api/manifest.json') {
31
38
  const manifests = await this.getManifests()
32
39
  await addTimestamp(manifests)
33
- res.write(JSON.stringify(manifests))
34
- return res.end()
40
+ return sendJSON(res, manifests)
35
41
  }
42
+
43
+ if (path === '/api/deps.json') {
44
+ return sendJSON(res, {})
45
+ }
46
+
36
47
  next()
37
48
  })
38
49
 
@@ -0,0 +1,32 @@
1
+ import { describe, it, expect, afterEach } from '@jest/globals'
2
+ import { createServer } from 'vite'
3
+ import path from 'path'
4
+ import vitePluginOxManifests from '../../index'
5
+ import { getPort } from '../util'
6
+ import axios from 'axios'
7
+
8
+ const PORT = getPort()
9
+ const __dirname = path.dirname(new URL(import.meta.url).pathname)
10
+
11
+ describe('deps', function () {
12
+ let server
13
+
14
+ afterEach(async function () {
15
+ if (server) {
16
+ await server.close()
17
+ server = null
18
+ }
19
+ })
20
+
21
+ it('deliver empty deps in dev mode', async function () {
22
+ server = await createServer({
23
+ root: __dirname,
24
+ logLevel: 'silent',
25
+ plugins: [vitePluginOxManifests({})]
26
+ })
27
+ await server.listen(PORT)
28
+
29
+ const { data: deps } = await axios({ baseURL: `http://localhost:${PORT}/api/deps.json` })
30
+ expect(deps).toEqual({})
31
+ })
32
+ })
@@ -40,13 +40,10 @@ describe('Multiple dictionaries gettext scenario', function () {
40
40
 
41
41
  const manifestFile = getFileFromBundle('manifest', bundle)
42
42
  const manifests = JSON.parse(manifestFile.source)
43
- expect(manifests).toHaveLength(3)
44
- expect(manifests[0].namespace).toEqual('test')
45
- expect(manifests[0].path).toContain('register')
46
- expect(manifests[1].namespace).toEqual('i18n')
47
- expect(manifests[1].path).toContain('foobar')
48
- expect(manifests[2].namespace).toEqual('i18n')
49
- expect(manifests[2].path).toContain('i18n')
43
+ expect(Object.keys(manifests)).toHaveLength(8)
44
+ expect(manifests['register.js'].meta.manifests[0].namespace).toEqual('test')
45
+ expect(manifests['../../foobar'].meta.manifests[0].namespace).toEqual('i18n')
46
+ expect(manifests['../../i18n'].meta.manifests[0].namespace).toEqual('i18n')
50
47
  })
51
48
 
52
49
  it('works in dev mode', async function () {
@@ -33,11 +33,9 @@ describe('Simple gettext scenario', function () {
33
33
 
34
34
  const manifestFile = getFileFromBundle('manifest', bundle)
35
35
  const manifests = JSON.parse(manifestFile.source)
36
- expect(manifests).toHaveLength(2)
37
- expect(manifests[0].namespace).toEqual('test')
38
- expect(manifests[0].path).toContain('register')
39
- expect(manifests[1].namespace).toEqual('i18n')
40
- expect(manifests[1].path).toContain('i18n')
36
+ expect(Object.keys(manifests)).toHaveLength(4)
37
+ expect(manifests['register.js'].meta.manifests[0].namespace).toEqual('test')
38
+ expect(manifests['../../i18n'].meta.manifests[0].namespace).toEqual('i18n')
41
39
  })
42
40
 
43
41
  it('works in dev mode', async function () {
@@ -19,9 +19,8 @@ describe('Manifest with dependencies', function () {
19
19
 
20
20
  const manifestFile = getFileFromBundle('manifest', bundle)
21
21
  const manifests = JSON.parse(manifestFile.source)
22
- expect(manifests).toHaveLength(1)
23
- expect(manifests[0].namespace).toEqual('test')
24
- expect(manifests[0].path).toContain('register')
25
- expect(manifests[0].dependencies).toHaveLength(1)
22
+ expect(Object.keys(manifests)).toHaveLength(1)
23
+ expect(manifests['register.js'].meta.manifests[0].namespace).toEqual('test')
24
+ expect(manifests['register.js'].css).toHaveLength(1)
26
25
  })
27
26
  })
@@ -22,8 +22,7 @@ describe('Manifest with dynamic dependencies', function () {
22
22
  const manifestFile = getFileFromBundle('manifest', bundle)
23
23
  const manifests = JSON.parse(manifestFile.source)
24
24
 
25
- const [registerModule] = manifests.filter(m => m.namespace === 'test')
26
- expect(registerModule.dependencies).toHaveLength(1)
27
- expect(manifests.filter(m => m.namespace === 'dynamicEntry')).toHaveLength(1)
25
+ expect(manifests['register.js'].css).toHaveLength(1)
26
+ expect(manifests['register.js'].dynamicImports).toHaveLength(1)
28
27
  })
29
28
  })
@@ -38,23 +38,14 @@ describe('Multiple manifest scenario', function () {
38
38
 
39
39
  const manifestFile = getFileFromBundle('manifest', bundle)
40
40
  const manifests = JSON.parse(manifestFile.source)
41
- expect(manifests).toHaveLength(4)
42
-
43
- const entry1Index = manifests.findIndex(m => m.path.indexOf('entry1') >= 0)
44
- expect(manifests[entry1Index].namespace).toEqual('test')
45
- expect(manifests[entry1Index].path).toContain('entry1')
41
+ expect(Object.keys(manifests)).toHaveLength(2)
46
42
 
47
- const otherNSIndex = manifests.findIndex(m => m.namespace === 'other')
48
- expect(manifests[otherNSIndex].namespace).toEqual('other')
49
- expect(manifests[otherNSIndex].path).toContain('entry2')
43
+ expect(manifests['entry1.js'].meta.manifests[0].namespace).toEqual('test')
50
44
 
51
- const lastIndex = manifests.findIndex(({ path, namespace }) => path.indexOf('entry2') >= 0 && namespace === 'test')
52
- expect(manifests[lastIndex].namespace).toEqual('test')
53
- expect(manifests[lastIndex].path).toContain('entry2')
54
-
55
- const settingsIndex = manifests.findIndex(m => m.namespace === 'settings')
56
- expect(manifests[settingsIndex].namespace).toEqual('settings')
57
- expect(manifests[settingsIndex].path).toContain('entry2')
45
+ expect(manifests['sub/entry2.js'].meta.manifests).toHaveLength(3)
46
+ expect(manifests['sub/entry2.js'].meta.manifests.find(m => m.namespace === 'other').namespace).toEqual('other')
47
+ expect(manifests['sub/entry2.js'].meta.manifests.find(m => m.namespace === 'test').namespace).toEqual('test')
48
+ expect(manifests['sub/entry2.js'].meta.manifests.find(m => m.namespace === 'settings').namespace).toEqual('settings')
58
49
  })
59
50
 
60
51
  it('works in dev mode', async function () {
@@ -30,9 +30,8 @@ describe('Simple manifest scenario', function () {
30
30
 
31
31
  const manifestFile = getFileFromBundle('manifest', bundle)
32
32
  const manifests = JSON.parse(manifestFile.source)
33
- expect(manifests).toHaveLength(1)
34
- expect(manifests[0].namespace).toEqual('test')
35
- expect(manifests[0].path).toContain('register')
33
+ expect(Object.keys(manifests)).toHaveLength(1)
34
+ expect(manifests['register.js'].meta.manifests[0].namespace).toEqual('test')
36
35
  })
37
36
 
38
37
  it('works in dev mode', async function () {
@@ -48,7 +47,7 @@ describe('Simple manifest scenario', function () {
48
47
  const res = await axios({ baseURL: `http://localhost:${PORT}/api/manifest.json` })
49
48
  const manifests = res.data
50
49
 
51
- expect(manifests).toHaveLength(1)
50
+ expect(Object.keys(manifests)).toHaveLength(1)
52
51
  expect(manifests[0].namespace).toEqual('test')
53
52
  expect(manifests[0].path).toContain('register')
54
53
  })
@@ -22,12 +22,11 @@ describe('Preload helper bundled', function () {
22
22
  })
23
23
 
24
24
  const index = getFileFromBundle('index', bundle)
25
- // eslint-disable-next-line no-template-curly-in-string
26
- expect(index.code).toContain('dep = dep.startsWith("/") ? `.${dep}` : `./${dep}`')
25
+ expect(index.code).toContain('const __vitePreload = function preload')
27
26
 
28
27
  const manifestFile = getFileFromBundle('manifest', bundle)
29
28
  const manifests = JSON.parse(manifestFile.source)
30
- expect(manifests).toHaveLength(2)
31
- expect(manifests[0].dependencies).toBeUndefined()
29
+ expect(Object.keys(manifests)).toHaveLength(2)
30
+ expect(manifests['index.js'].dependencies).toBeUndefined()
32
31
  })
33
32
  })
@@ -23,14 +23,12 @@ describe('Preload helper separate', function () {
23
23
  })]
24
24
  })
25
25
  const preloadHelper = getFileFromBundle('preload', bundle)
26
- // eslint-disable-next-line no-template-curly-in-string
27
- expect(preloadHelper.code).toContain('dep = dep.startsWith("/") ? `.${dep}` : `./${dep}`')
26
+ expect(preloadHelper.code).toContain('const __vitePreload = function preload')
28
27
 
29
28
  const manifestFile = getFileFromBundle('manifest', bundle)
30
29
  const manifests = JSON.parse(manifestFile.source)
31
- expect(manifests).toHaveLength(3)
32
- const [mainManifest] = manifests.filter(m => m.namespace === 'test')
33
- expect(mainManifest.dependencies).toHaveLength(1)
34
- expect(mainManifest.dependencies[0]).toContain('preload-helper')
30
+ expect(Object.keys(manifests)).toHaveLength(4)
31
+ expect(manifests['index.js'].imports).toHaveLength(1)
32
+ expect(manifests['index.js'].imports[0]).toContain('preload-helper')
35
33
  })
36
34
  })
@@ -34,8 +34,8 @@ describe('Settings like in core', function () {
34
34
  })
35
35
  const manifestFile = getFileFromBundle('manifest', bundle)
36
36
  const manifests = JSON.parse(manifestFile.source)
37
- expect(manifests).toHaveLength(2)
38
- expect(manifests[1].namespace).toEqual('settings')
37
+ expect(Object.keys(manifests)).toHaveLength(2)
38
+ expect(manifests['settings.js'].meta.manifests[0].namespace).toEqual('settings')
39
39
  })
40
40
 
41
41
  it('works in dev mode', async function () {
@@ -37,8 +37,8 @@ describe('Settings external', function () {
37
37
 
38
38
  const manifestFile = getFileFromBundle('manifest', bundle)
39
39
  const manifests = JSON.parse(manifestFile.source)
40
- expect(manifests).toHaveLength(2)
41
- expect(manifests[1].namespace).toEqual('settings')
40
+ expect(Object.keys(manifests)).toHaveLength(2)
41
+ expect(manifests['settings.js'].meta.manifests[0].namespace).toEqual('settings')
42
42
  })
43
43
 
44
44
  it('works in dev mode', async function () {
@@ -24,7 +24,7 @@ describe('Settings typescript', function () {
24
24
  })
25
25
  const manifestFile = getFileFromBundle('manifest', bundle)
26
26
  const manifests = JSON.parse(manifestFile.source)
27
- expect(manifests).toHaveLength(2)
28
- expect(manifests[1].namespace).toEqual('settings')
27
+ expect(Object.keys(manifests)).toHaveLength(2)
28
+ expect(manifests['settings.ts'].meta.manifests[0].namespace).toEqual('settings')
29
29
  })
30
30
  })
@@ -22,8 +22,8 @@ describe('Settings like in core, with requirements', function () {
22
22
  })
23
23
  const manifestFile = getFileFromBundle('manifest', bundle)
24
24
  const manifests = JSON.parse(manifestFile.source)
25
- expect(manifests).toHaveLength(2)
26
- expect(manifests[1].namespace).toEqual('settings')
27
- expect(manifests[1].requires).toEqual('cap')
25
+ expect(Object.keys(manifests)).toHaveLength(2)
26
+ expect(manifests['settings.js'].meta.manifests[0].namespace).toEqual('settings')
27
+ expect(manifests['settings.js'].meta.manifests[0].requires).toEqual('cap')
28
28
  })
29
29
  })
@@ -0,0 +1,64 @@
1
+ import { describe, it, expect, afterEach } from '@jest/globals'
2
+ import { createServer } from 'vite'
3
+ import path from 'path'
4
+ import vitePluginOxManifests from '../../index'
5
+ import { getPort } from '../util'
6
+ import axios from 'axios'
7
+ import express from 'express'
8
+
9
+ const __dirname = path.dirname(new URL(import.meta.url).pathname)
10
+ const PORT = getPort()
11
+ const MANIFEST_PORT = getPort()
12
+ const OTHER_PORT = getPort()
13
+
14
+ describe('Additional upstream manifests', function () {
15
+ let server, manifestServer, otherServer
16
+
17
+ afterEach(async function () {
18
+ if (server) {
19
+ await server.close()
20
+ server = null
21
+ }
22
+ if (manifestServer) {
23
+ await manifestServer.close()
24
+ manifestServer = null
25
+ }
26
+ if (otherServer) {
27
+ await otherServer.close()
28
+ otherServer = null
29
+ }
30
+ })
31
+
32
+ it('works in dev mode', async function () {
33
+ server = await createServer({
34
+ root: __dirname,
35
+ logLevel: 'silent',
36
+ plugins: [vitePluginOxManifests({
37
+ appsuiteUrl: `http://localhost:${MANIFEST_PORT}`,
38
+ additionalManifestUrls: [`http://localhost:${OTHER_PORT}/path/to/file.json`]
39
+ })]
40
+ })
41
+ await server.listen(PORT)
42
+
43
+ // create a manifest server
44
+ const app = express()
45
+ manifestServer = app.listen(MANIFEST_PORT)
46
+ app.get('/api/manifest.json', (req, res) => {
47
+ res.json([{ namespace: 'server' }])
48
+ })
49
+
50
+ // create other server with random endpoint
51
+ const app2 = express()
52
+ otherServer = app2.listen(OTHER_PORT)
53
+ app2.get('/path/to/file.json', (req, res) => {
54
+ res.json([{ namespace: 'other' }])
55
+ })
56
+
57
+ const res = await axios({ baseURL: `http://localhost:${PORT}/api/manifest.json` })
58
+ const manifests = res.data
59
+ expect(Object.keys(manifests)).toHaveLength(3)
60
+ expect(manifests.map(m => m.namespace)).toContain('test')
61
+ expect(manifests.map(m => m.namespace)).toContain('server')
62
+ expect(manifests.map(m => m.namespace)).toContain('other')
63
+ })
64
+ })
@@ -0,0 +1,3 @@
1
+ {
2
+ "namespace": "test"
3
+ }
@@ -0,0 +1 @@
1
+ console.log('Hello world')