proscenium 0.5.0-x86_64-darwin → 0.6.0-x86_64-darwin

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f905fa040049f8ad4f5863b705c9f99f74cda1e38c3cbb9325e7bada262e36d
4
- data.tar.gz: 73ef3f88ab3a1c6510275be8df6c0ca982d483685deff51764c9ae4ae057acef
3
+ metadata.gz: 87f97093007fdb59a8a3d34075e2570f6871c63bb404d3263fb3b21fd25d6a4e
4
+ data.tar.gz: c6a5f64992acaa9bff23de20ae9c317db60c504fa2f62d00e8bbab991b430892
5
5
  SHA512:
6
- metadata.gz: da5cb634c091dba3d8a86e571dae54a1683b45620076ebbe74ac1f2990e1a5b934310704cd16cc364f4fc3b3edd6900561f5a11d20b0065379546af349a6ec17
7
- data.tar.gz: 2468099ba8d90572e876a54e7f1a66c1b1cf77c243edcbd57a9b89a25e71f06c501c9c61133b6249082efc12675a5a24fe6d43750c2ab12422bc688a6b73b2b2
6
+ metadata.gz: 3519fae0fb5c95d5cc50dbce5e665cdbef81c484236605ecbe7ea18f16a9803c65ba70051d33296bd7ea3ae2e4d9f1e32b7cf502467b985a9f3f607af749fea5
7
+ data.tar.gz: 351f524bbb591d39ebf823d9939932511da63d61e971f604c2660f64ca1dd3635a2ae6935064cad359a8fd1336859b77e98fd9598d95492d2c23bb36e2f7f635
data/README.md CHANGED
@@ -117,7 +117,10 @@ as you could end up swallowing everything, resulting in a very large file.
117
117
  ## Import Map
118
118
 
119
119
  Import map for both JS and CSS is supported out of the box, and works with no regard to the browser
120
- version being used. Just create `config/import_map.json`:
120
+ version being used. This is because the import map is parsed and resolved by Proscenium on the
121
+ server.
122
+
123
+ Just create `config/import_map.json`:
121
124
 
122
125
  ```json
123
126
  {
@@ -157,6 +160,18 @@ env => ({
157
160
  })
158
161
  ```
159
162
 
163
+ ### Aliasing
164
+
165
+ You can also use the import map to define aliases:
166
+
167
+ ```json
168
+ {
169
+ "imports": {
170
+ "react": "preact/compact",
171
+ }
172
+ }
173
+ ```
174
+
160
175
  ## Side Loading
161
176
 
162
177
  Proscenium has built in support for automatically side loading JS and CSS with your views and
data/bin/esbuild CHANGED
Binary file
@@ -1,12 +1,12 @@
1
1
  import { expandGlob } from 'std/fs/mod.ts'
2
- import postcss, { CssSyntaxError } from 'postcss'
2
+ import postcss from 'postcss'
3
3
 
4
4
  export default async (root, path) => {
5
5
  let tmpFile
6
6
  let contents
7
7
 
8
8
  const mixinFiles = []
9
- for await (const file of expandGlob(`lib/**/*.mixin.css`, { root })) {
9
+ for await (const file of expandGlob('lib/**/*.mixin.css', { root, globstar: true })) {
10
10
  mixinFiles.push(file.path)
11
11
  }
12
12
 
@@ -0,0 +1,178 @@
1
+ import { tryURLParse, tryURLLikeSpecifierParse } from './utils.js'
2
+
3
+ /**
4
+ * @param {ImportMap} input
5
+ * @param {URL} baseURL
6
+ * @returns {ParsedImportMap}
7
+ */
8
+ export function parse(input, baseURL) {
9
+ if (!isJSONObject(input)) {
10
+ throw new TypeError('Import map JSON must be an object.')
11
+ }
12
+
13
+ if (!(baseURL instanceof URL)) {
14
+ throw new TypeError('Missing base URL or base URL is not a URL')
15
+ }
16
+
17
+ let sortedAndNormalizedImports = {}
18
+ if ('imports' in input) {
19
+ if (!input.imports || !isJSONObject(input.imports)) {
20
+ throw new TypeError("Import map's imports value must be an object.")
21
+ }
22
+ sortedAndNormalizedImports = sortAndNormalizeSpecifierMap(input.imports, baseURL)
23
+ }
24
+
25
+ let sortedAndNormalizedScopes = {}
26
+ if ('scopes' in input) {
27
+ if (!input.scopes || !isJSONObject(input.scopes)) {
28
+ throw new TypeError("Import map's scopes value must be an object.")
29
+ }
30
+ sortedAndNormalizedScopes = sortAndNormalizeScopes(input.scopes, baseURL)
31
+ }
32
+
33
+ const badTopLevelKeys = new Set(Object.keys(input))
34
+ badTopLevelKeys.delete('imports')
35
+ badTopLevelKeys.delete('scopes')
36
+
37
+ for (const badKey of badTopLevelKeys) {
38
+ throw new TypeError(
39
+ `Invalid top-level key "${badKey}". Only "imports" and "scopes" can be present.`
40
+ )
41
+ }
42
+
43
+ // Always have these two keys, and exactly these two keys, in the result.
44
+ return {
45
+ imports: sortedAndNormalizedImports,
46
+ scopes: sortedAndNormalizedScopes
47
+ }
48
+ }
49
+
50
+ /**
51
+ * @param {string} input
52
+ * @param {URL} baseURL
53
+ * @returns {ParsedImportMap}
54
+ */
55
+ export function parseFromString(input, baseURL) {
56
+ const importMap = JSON.parse(input)
57
+ return parse(importMap, baseURL)
58
+ }
59
+
60
+ /**
61
+ * @param {string} a
62
+ * @param {string} b
63
+ */
64
+ function codeUnitCompare(a, b) {
65
+ if (a > b) {
66
+ return 1
67
+ }
68
+
69
+ if (b > a) {
70
+ return -1
71
+ }
72
+
73
+ throw new Error('This should never be reached because this is only used on JSON object keys')
74
+ }
75
+
76
+ /**
77
+ * @param {string} specifierKey
78
+ * @param {URL} baseURL
79
+ * @returns {string | undefined}
80
+ */
81
+ function normalizeSpecifierKey(specifierKey, baseURL) {
82
+ // Ignore attempts to use the empty string as a specifier key
83
+ if (specifierKey === '') {
84
+ throw new TypeError(`Invalid empty string specifier key.`)
85
+ }
86
+
87
+ const url = tryURLLikeSpecifierParse(specifierKey, baseURL)
88
+ if (url) return url.href
89
+
90
+ return specifierKey
91
+ }
92
+
93
+ /**
94
+ * @param {SpecifierMap} obj
95
+ * @param {URL} baseURL
96
+ * @returns {ParsedSpecifierMap}
97
+ */
98
+ function sortAndNormalizeSpecifierMap(obj, baseURL) {
99
+ if (!isJSONObject(obj)) {
100
+ throw new TypeError('Expect map to be a JSON object.')
101
+ }
102
+
103
+ const normalized = {}
104
+
105
+ for (const [specifierKey, value] of Object.entries(obj)) {
106
+ const normalizedSpecifierKey = normalizeSpecifierKey(specifierKey, baseURL)
107
+ if (!normalizedSpecifierKey) continue
108
+
109
+ if (typeof value !== 'string') {
110
+ throw new TypeError(
111
+ `Invalid address ${JSON.stringify(value)} for the specifier key "${specifierKey}". ` +
112
+ `Addresses must be strings.`
113
+ )
114
+ }
115
+
116
+ const addressURL = tryURLLikeSpecifierParse(value, baseURL)
117
+ if (!addressURL) {
118
+ // Support aliases.
119
+ // console.warn(`Invalid address "${value}" for the specifier key "${specifierKey}".`)
120
+ normalized[normalizedSpecifierKey] = value
121
+ continue
122
+ }
123
+
124
+ if (specifierKey.endsWith('/') && !addressURL.href.endsWith('/')) {
125
+ throw new TypeError(
126
+ `Invalid address "${addressURL.href}" for package specifier key "${specifierKey}". ` +
127
+ `Package addresses must end with "/".`
128
+ )
129
+ }
130
+
131
+ normalized[normalizedSpecifierKey] = addressURL
132
+ }
133
+
134
+ const sortedAndNormalized = {}
135
+ const sortedKeys = Object.keys(normalized).sort((a, b) => codeUnitCompare(b, a))
136
+ for (const key of sortedKeys) {
137
+ sortedAndNormalized[key] = normalized[key]
138
+ }
139
+
140
+ return sortedAndNormalized
141
+ }
142
+
143
+ /**
144
+ * @param {ScopesMap} obj
145
+ * @param {URL} baseURL
146
+ */
147
+ function sortAndNormalizeScopes(obj, baseURL) {
148
+ const normalized = {}
149
+ for (const [scopePrefix, potentialSpecifierMap] of Object.entries(obj)) {
150
+ if (!isJSONObject(potentialSpecifierMap)) {
151
+ throw new TypeError(`The value for the "${scopePrefix}" scope prefix must be an object.`)
152
+ }
153
+
154
+ const scopePrefixURL = tryURLParse(scopePrefix, baseURL)
155
+ if (!scopePrefixURL) {
156
+ throw new TypeError(`Invalid scope "${scopePrefix}" (parsed against base URL "${baseURL}").`)
157
+ }
158
+
159
+ const normalizedScopePrefix = scopePrefixURL.href
160
+ normalized[normalizedScopePrefix] = sortAndNormalizeSpecifierMap(potentialSpecifierMap, baseURL)
161
+ }
162
+
163
+ const sortedAndNormalized = {}
164
+ const sortedKeys = Object.keys(normalized).sort((a, b) => codeUnitCompare(b, a))
165
+ for (const key of sortedKeys) {
166
+ sortedAndNormalized[key] = normalized[key]
167
+ }
168
+
169
+ return sortedAndNormalized
170
+ }
171
+
172
+ /**
173
+ * @param {*} value
174
+ * @returns {value is object}
175
+ */
176
+ function isJSONObject(value) {
177
+ return typeof value === 'object' && value != null && !Array.isArray(value)
178
+ }
@@ -1,5 +1,10 @@
1
+ //
2
+ // Taken almost verbatim from https://github.com/open-wc/open-wc/tree/master/packages/import-maps-resolve
3
+ // Slightly modified to support aliases.
4
+ //
5
+
1
6
  import { join } from 'std/path/mod.ts'
2
- import { parseFromString } from 'import-maps/resolve'
7
+ import { parseFromString } from './parser.js'
3
8
 
4
9
  const baseURL = new URL('file://')
5
10
 
@@ -16,7 +21,7 @@ class ImportMapError extends Error {
16
21
  }
17
22
  }
18
23
 
19
- export function readImportMap(fileName, rootDir) {
24
+ export default function (fileName, rootDir) {
20
25
  let importMap
21
26
 
22
27
  if (fileName) {
@@ -0,0 +1,95 @@
1
+ import { tryURLLikeSpecifierParse, tryURLParse } from './utils.js'
2
+
3
+ /**
4
+ * @param {string} specifier
5
+ * @param {ParsedImportMap} parsedImportMap
6
+ * @param {URL} scriptURL
7
+ * @returns {{ resolvedImport: URL | null, matched: boolean }}
8
+ */
9
+ export default function (specifier, parsedImportMap, scriptURL) {
10
+ const asURL = tryURLLikeSpecifierParse(specifier, scriptURL)
11
+ const normalizedSpecifier = asURL ? asURL.href : specifier
12
+ const scriptURLString = scriptURL.href
13
+
14
+ for (const [scopePrefix, scopeImports] of Object.entries(parsedImportMap.scopes || {})) {
15
+ if (
16
+ scopePrefix === scriptURLString ||
17
+ (scopePrefix.endsWith('/') && scriptURLString.startsWith(scopePrefix))
18
+ ) {
19
+ const scopeImportsMatch = resolveImportsMatch(normalizedSpecifier, scopeImports)
20
+
21
+ if (scopeImportsMatch) {
22
+ return { resolvedImport: scopeImportsMatch, matched: true }
23
+ }
24
+ }
25
+ }
26
+
27
+ const topLevelImportsMatch = resolveImportsMatch(
28
+ normalizedSpecifier,
29
+ parsedImportMap.imports || {}
30
+ )
31
+
32
+ if (topLevelImportsMatch) {
33
+ return { resolvedImport: topLevelImportsMatch, matched: true }
34
+ }
35
+
36
+ // The specifier was able to be turned into a URL, but wasn't remapped into anything.
37
+ if (asURL) {
38
+ return { resolvedImport: asURL, matched: false }
39
+ }
40
+
41
+ return { resolvedImport: null, matched: false }
42
+ }
43
+
44
+ /**
45
+ * @param {string} normalizedSpecifier
46
+ * @param {ParsedSpecifierMap} specifierMap
47
+ */
48
+ function resolveImportsMatch(normalizedSpecifier, specifierMap) {
49
+ for (const [specifierKey, resolutionResult] of Object.entries(specifierMap)) {
50
+ // Exact-match case
51
+ if (specifierKey === normalizedSpecifier) {
52
+ if (!resolutionResult) {
53
+ throw new TypeError(`Blocked by a null entry for "${specifierKey}"`)
54
+ }
55
+
56
+ if (!(resolutionResult instanceof URL)) {
57
+ // Support aliases.
58
+ // throw new TypeError(`Expected ${resolutionResult} to be a URL.`)
59
+ }
60
+
61
+ return resolutionResult
62
+ }
63
+
64
+ // Package prefix-match case
65
+ if (specifierKey.endsWith('/') && normalizedSpecifier.startsWith(specifierKey)) {
66
+ if (!resolutionResult) {
67
+ throw new TypeError(`Blocked by a null entry for "${specifierKey}"`)
68
+ }
69
+
70
+ if (!(resolutionResult instanceof URL)) {
71
+ throw new TypeError(`Expected ${resolutionResult} to be a URL.`)
72
+ }
73
+
74
+ const afterPrefix = normalizedSpecifier.substring(specifierKey.length)
75
+
76
+ // Enforced by parsing
77
+ if (!resolutionResult.href.endsWith('/')) {
78
+ throw new TypeError(`Expected ${resolutionResult.href} to end with a '/'.`)
79
+ }
80
+
81
+ const url = tryURLParse(afterPrefix, resolutionResult)
82
+ if (!url) {
83
+ throw new TypeError(`Failed to resolve prefix-match relative URL for "${specifierKey}"`)
84
+ }
85
+
86
+ if (!(url instanceof URL)) {
87
+ throw new TypeError(`Expected ${url} to be a URL.`)
88
+ }
89
+
90
+ return url
91
+ }
92
+ }
93
+
94
+ return undefined
95
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @param {string} string
3
+ * @param {URL} [baseURL]
4
+ * @returns {URL | undefined}
5
+ */
6
+ export function tryURLParse(string, baseURL) {
7
+ try {
8
+ return new URL(string, baseURL)
9
+ } catch (e) {
10
+ return undefined
11
+ }
12
+ }
13
+
14
+ /**
15
+ * @param {string} specifier
16
+ * @param {URL} baseURL
17
+ * @returns {URL | undefined}
18
+ */
19
+ export function tryURLLikeSpecifierParse(specifier, baseURL) {
20
+ if (specifier.startsWith('/') || specifier.startsWith('./') || specifier.startsWith('../')) {
21
+ return tryURLParse(specifier, baseURL)
22
+ }
23
+
24
+ return tryURLParse(specifier)
25
+ }
@@ -1,8 +1,9 @@
1
1
  import { join, resolve } from 'std/path/mod.ts'
2
- import { resolve as resolveFromImportMap } from 'import-maps/resolve'
2
+ import resolveFromImportMap from './import_map/resolver.js'
3
3
  import { cache } from 'cache'
4
4
 
5
5
  import setup from './setup_plugin.js'
6
+ import { isBareModule } from '../../utils.js'
6
7
 
7
8
  const importKinds = ['import-statement', 'dynamic-import', 'require-call', 'import-rule']
8
9
 
@@ -108,16 +109,21 @@ export default setup('resolve', (build, options) => {
108
109
  }
109
110
 
110
111
  const { matched, resolvedImport } = resolveFromImportMap(params.path, importMap, baseURL)
112
+ // console.log({ importMap, matched, resolvedImport })
111
113
 
112
114
  if (matched) {
113
- if (resolvedImport.protocol === 'file:') {
114
- params.path = resolvedImport.pathname
115
- } else {
116
- if (params.importer.endsWith('.css')) {
117
- return { path: resolvedImport.href, external: true }
115
+ if (resolvedImport instanceof URL) {
116
+ if (resolvedImport.protocol === 'file:') {
117
+ params.path = resolvedImport.pathname
118
+ } else {
119
+ if (params.importer.endsWith('.css')) {
120
+ return { path: resolvedImport.href, external: true }
121
+ }
122
+
123
+ return { path: `/url:${encodeURIComponent(resolvedImport.href)}`, external: true }
118
124
  }
119
-
120
- return { path: `/url:${encodeURIComponent(resolvedImport.href)}`, external: true }
125
+ } else {
126
+ result.path = resolvedImport
121
127
  }
122
128
  }
123
129
  }
@@ -199,7 +205,3 @@ export default setup('resolve', (build, options) => {
199
205
  return result
200
206
  }
201
207
  })
202
-
203
- function isBareModule(mod) {
204
- return !mod.startsWith('/') && !mod.startsWith('.')
205
- }
@@ -4,7 +4,7 @@ import { expandGlob } from 'std/fs/mod.ts'
4
4
  import { join, isGlob, resolve, dirname, fromFileUrl } from 'std/path/mod.ts'
5
5
  import { build, stop } from 'esbuild'
6
6
 
7
- import { readImportMap } from './esbuild/import_map.js'
7
+ import readImportMap from './esbuild/import_map/read.js'
8
8
  import envPlugin from './esbuild/env_plugin.js'
9
9
  import cssPlugin from './esbuild/css_plugin.js'
10
10
  import resolvePlugin from './esbuild/resolve_plugin.js'
@@ -6,3 +6,7 @@ export async function fileExists(path) {
6
6
  return false
7
7
  }
8
8
  }
9
+
10
+ export function isBareModule(mod) {
11
+ return !mod.startsWith('/') && !mod.startsWith('.')
12
+ }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Proscenium
4
- VERSION = '0.5.0'
4
+ VERSION = '0.6.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: proscenium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: x86_64-darwin
6
6
  authors:
7
7
  - Joel Moss
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-08 00:00:00.000000000 Z
11
+ date: 2022-11-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actioncable
@@ -167,7 +167,10 @@ files:
167
167
  - lib/proscenium/compilers/esbuild/css_plugin.js
168
168
  - lib/proscenium/compilers/esbuild/env_plugin.js
169
169
  - lib/proscenium/compilers/esbuild/http_bundle_plugin.js
170
- - lib/proscenium/compilers/esbuild/import_map.js
170
+ - lib/proscenium/compilers/esbuild/import_map/parser.js
171
+ - lib/proscenium/compilers/esbuild/import_map/read.js
172
+ - lib/proscenium/compilers/esbuild/import_map/resolver.js
173
+ - lib/proscenium/compilers/esbuild/import_map/utils.js
171
174
  - lib/proscenium/compilers/esbuild/resolve_plugin.js
172
175
  - lib/proscenium/compilers/esbuild/setup_plugin.js
173
176
  - lib/proscenium/compilers/esbuild/solidjs_plugin.js