@uwdata/mosaic-spec 0.7.0 → 0.8.0

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.
Files changed (154) hide show
  1. package/dist/mosaic-schema.json +186153 -0
  2. package/dist/mosaic-spec.js +3387 -4641
  3. package/dist/mosaic-spec.min.js +23 -23
  4. package/dist/types/ast/ASTNode.d.ts +26 -0
  5. package/dist/types/ast/DataNode.d.ts +60 -0
  6. package/dist/types/ast/ExpressionNode.d.ts +15 -0
  7. package/dist/types/ast/HConcatNode.d.ts +10 -0
  8. package/dist/types/ast/HSpaceNode.d.ts +11 -0
  9. package/dist/types/ast/InputNode.d.ts +8 -0
  10. package/dist/types/ast/LiteralNode.d.ts +7 -0
  11. package/dist/types/ast/OptionsNode.d.ts +10 -0
  12. package/dist/types/ast/ParamNode.d.ts +9 -0
  13. package/dist/types/ast/ParamRefNode.d.ts +8 -0
  14. package/dist/types/ast/PlotAttributeNode.d.ts +18 -0
  15. package/dist/types/ast/PlotFromNode.d.ts +9 -0
  16. package/dist/types/ast/PlotInteractorNode.d.ts +8 -0
  17. package/dist/types/ast/PlotLegendNode.d.ts +10 -0
  18. package/dist/types/ast/PlotMarkNode.d.ts +10 -0
  19. package/dist/types/ast/PlotNode.d.ts +11 -0
  20. package/dist/types/ast/SelectionNode.d.ts +12 -0
  21. package/dist/types/ast/SpecNode.d.ts +10 -0
  22. package/dist/types/ast/TransformNode.d.ts +13 -0
  23. package/dist/types/ast/VConcatNode.d.ts +10 -0
  24. package/dist/types/ast/VSpaceNode.d.ts +11 -0
  25. package/dist/types/ast-to-dom.d.ts +38 -0
  26. package/dist/types/ast-to-esm.d.ts +64 -0
  27. package/dist/types/config/components.d.ts +4 -0
  28. package/dist/types/config/extensions.d.ts +9 -0
  29. package/dist/types/config/inputs.d.ts +5 -0
  30. package/dist/types/config/plots.d.ts +34 -0
  31. package/dist/types/config/transforms.d.ts +4 -0
  32. package/dist/types/constants.d.ts +30 -0
  33. package/dist/types/index.d.ts +29 -0
  34. package/dist/types/parse-spec.d.ts +86 -0
  35. package/dist/types/spec/CSSStyles.d.ts +3 -0
  36. package/dist/types/spec/Data.d.ts +165 -0
  37. package/dist/types/spec/Expression.d.ts +27 -0
  38. package/dist/types/spec/HConcat.d.ts +8 -0
  39. package/dist/types/spec/HSpace.d.ts +9 -0
  40. package/dist/types/spec/Input.d.ts +182 -0
  41. package/dist/types/spec/Param.d.ts +51 -0
  42. package/dist/types/spec/Plot.d.ts +14 -0
  43. package/dist/types/spec/PlotAttribute.d.ts +1522 -0
  44. package/dist/types/spec/PlotFrom.d.ts +20 -0
  45. package/dist/types/spec/PlotInteractor.d.ts +8 -0
  46. package/dist/types/spec/PlotLegend.d.ts +68 -0
  47. package/dist/types/spec/PlotMark.d.ts +26 -0
  48. package/dist/types/spec/PlotTypes.d.ts +293 -0
  49. package/dist/types/spec/Spec.d.ts +45 -0
  50. package/dist/types/spec/Transform.d.ts +278 -0
  51. package/dist/types/spec/VConcat.d.ts +8 -0
  52. package/dist/types/spec/VSpace.d.ts +9 -0
  53. package/dist/types/spec/interactors/Highlight.d.ts +37 -0
  54. package/dist/types/spec/interactors/Interval1D.d.ts +63 -0
  55. package/dist/types/spec/interactors/Interval2D.d.ts +46 -0
  56. package/dist/types/spec/interactors/Nearest.d.ts +25 -0
  57. package/dist/types/spec/interactors/PanZoom.d.ts +58 -0
  58. package/dist/types/spec/interactors/Toggle.d.ts +51 -0
  59. package/dist/types/spec/marks/Area.d.ts +139 -0
  60. package/dist/types/spec/marks/Arrow.d.ts +94 -0
  61. package/dist/types/spec/marks/Axis.d.ts +281 -0
  62. package/dist/types/spec/marks/Bar.d.ts +150 -0
  63. package/dist/types/spec/marks/Cell.d.ts +57 -0
  64. package/dist/types/spec/marks/Contour.d.ts +23 -0
  65. package/dist/types/spec/marks/Delaunay.d.ts +86 -0
  66. package/dist/types/spec/marks/DenseLine.d.ts +27 -0
  67. package/dist/types/spec/marks/Density.d.ts +121 -0
  68. package/dist/types/spec/marks/Dot.d.ts +129 -0
  69. package/dist/types/spec/marks/Frame.d.ts +21 -0
  70. package/dist/types/spec/marks/Geo.d.ts +53 -0
  71. package/dist/types/spec/marks/Hexbin.d.ts +30 -0
  72. package/dist/types/spec/marks/Hexgrid.d.ts +25 -0
  73. package/dist/types/spec/marks/Image.d.ts +89 -0
  74. package/dist/types/spec/marks/Line.d.ts +82 -0
  75. package/dist/types/spec/marks/Link.d.ts +60 -0
  76. package/dist/types/spec/marks/Marks.d.ts +890 -0
  77. package/dist/types/spec/marks/Raster.d.ts +124 -0
  78. package/dist/types/spec/marks/Rect.d.ts +166 -0
  79. package/dist/types/spec/marks/Regression.d.ts +58 -0
  80. package/dist/types/spec/marks/Rule.d.ts +100 -0
  81. package/dist/types/spec/marks/Text.d.ts +106 -0
  82. package/dist/types/spec/marks/Tick.d.ts +61 -0
  83. package/dist/types/spec/marks/Vector.d.ts +99 -0
  84. package/dist/types/util.d.ts +12 -0
  85. package/jsconfig.json +10 -0
  86. package/package.json +16 -9
  87. package/src/ast/ASTNode.js +23 -2
  88. package/src/ast/DataNode.js +79 -21
  89. package/src/ast/HSpaceNode.js +1 -1
  90. package/src/ast/InputNode.js +1 -2
  91. package/src/ast/ParamNode.js +1 -2
  92. package/src/ast/PlotAttributeNode.js +1 -2
  93. package/src/ast/PlotInteractorNode.js +1 -2
  94. package/src/ast/PlotLegendNode.js +1 -2
  95. package/src/ast/PlotMarkNode.js +3 -2
  96. package/src/ast/TransformNode.js +1 -2
  97. package/src/ast/VSpaceNode.js +1 -1
  98. package/src/ast-to-dom.js +16 -7
  99. package/src/ast-to-esm.js +41 -12
  100. package/src/config/inputs.js +1 -0
  101. package/src/config/plots.js +4 -0
  102. package/src/index.js +4 -0
  103. package/src/parse-spec.js +38 -5
  104. package/src/spec/CSSStyles.ts +9 -0
  105. package/src/spec/Data.ts +184 -0
  106. package/src/spec/Expression.ts +31 -0
  107. package/src/spec/HConcat.ts +9 -0
  108. package/src/spec/HSpace.ts +9 -0
  109. package/src/spec/Input.ts +182 -0
  110. package/src/spec/Param.ts +68 -0
  111. package/src/spec/Plot.ts +15 -0
  112. package/src/spec/PlotAttribute.ts +1783 -0
  113. package/src/spec/PlotFrom.ts +23 -0
  114. package/src/spec/PlotInteractor.ts +25 -0
  115. package/src/spec/PlotLegend.ts +70 -0
  116. package/src/spec/PlotMark.ts +51 -0
  117. package/src/spec/PlotTypes.ts +519 -0
  118. package/src/spec/Spec.ts +63 -0
  119. package/src/spec/Transform.ts +394 -0
  120. package/src/spec/VConcat.ts +9 -0
  121. package/src/spec/VSpace.ts +9 -0
  122. package/src/spec/interactors/Highlight.ts +38 -0
  123. package/src/spec/interactors/Interval1D.ts +67 -0
  124. package/src/spec/interactors/Interval2D.ts +48 -0
  125. package/src/spec/interactors/Nearest.ts +28 -0
  126. package/src/spec/interactors/PanZoom.ts +65 -0
  127. package/src/spec/interactors/Toggle.ts +56 -0
  128. package/src/spec/marks/Area.ts +154 -0
  129. package/src/spec/marks/Arrow.ts +108 -0
  130. package/src/spec/marks/Axis.ts +305 -0
  131. package/src/spec/marks/Bar.ts +160 -0
  132. package/src/spec/marks/Cell.ts +62 -0
  133. package/src/spec/marks/Contour.ts +25 -0
  134. package/src/spec/marks/Delaunay.ts +95 -0
  135. package/src/spec/marks/DenseLine.ts +30 -0
  136. package/src/spec/marks/Density.ts +145 -0
  137. package/src/spec/marks/Dot.ts +147 -0
  138. package/src/spec/marks/Frame.ts +23 -0
  139. package/src/spec/marks/Geo.ts +58 -0
  140. package/src/spec/marks/Hexbin.ts +34 -0
  141. package/src/spec/marks/Hexgrid.ts +27 -0
  142. package/src/spec/marks/Image.ts +101 -0
  143. package/src/spec/marks/Line.ts +93 -0
  144. package/src/spec/marks/Link.ts +70 -0
  145. package/src/spec/marks/Marks.ts +1062 -0
  146. package/src/spec/marks/Raster.ts +145 -0
  147. package/src/spec/marks/Rect.ts +183 -0
  148. package/src/spec/marks/Regression.ts +63 -0
  149. package/src/spec/marks/Rule.ts +113 -0
  150. package/src/spec/marks/Text.ts +122 -0
  151. package/src/spec/marks/Tick.ts +69 -0
  152. package/src/spec/marks/Vector.ts +113 -0
  153. package/src/util.js +8 -0
  154. package/tsconfig.json +11 -0
@@ -0,0 +1,99 @@
1
+ import { ParamRef } from '../Param.js';
2
+ import { FrameAnchor } from '../PlotTypes.js';
3
+ import { ChannelValue, ChannelValueSpec, MarkData, MarkOptions } from './Marks.js';
4
+ /**
5
+ * The built-in vector shape implementations; one of:
6
+ *
7
+ * - *arrow* - a straight line with an open arrowhead at the end (↑)
8
+ * - *spike* - an isosceles triangle with a flat base (▲)
9
+ */
10
+ export type VectorShapeName = 'arrow' | 'spike';
11
+ /** How to draw a vector: either a named shape or a custom implementation. */
12
+ export type VectorShape = VectorShapeName;
13
+ /** Options for the vector mark. */
14
+ export interface VectorOptions extends MarkOptions {
15
+ /**
16
+ * The horizontal position of the vector’s anchor point; an optional channel
17
+ * bound to the *x* scale. Default depends on the **frameAnchor**.
18
+ */
19
+ x?: ChannelValueSpec;
20
+ /**
21
+ * The vertical position of the vector’s anchor point; an optional channel
22
+ * bound to the *y* scale. Default depends on the **frameAnchor**.
23
+ */
24
+ y?: ChannelValueSpec;
25
+ /**
26
+ * The vector shape’s radius, such as half the width of the *arrow*’s head or
27
+ * the *spike*’s base; a constant number in pixels. Defaults to 3.5 pixels.
28
+ */
29
+ r?: number | ParamRef;
30
+ /**
31
+ * The vector’s length; either an optional channel bound to the *length* scale
32
+ * or a constant number in pixels. Defaults to 12 pixels.
33
+ */
34
+ length?: ChannelValueSpec;
35
+ /**
36
+ * The vector’s orientation (rotation angle); either a constant number in
37
+ * degrees clockwise, or an optional channel (with no associated scale).
38
+ * Defaults to 0 degrees with the vector pointing up.
39
+ */
40
+ rotate?: ChannelValue;
41
+ /** The shape of the vector; a constant. Defaults to *arrow*. */
42
+ shape?: VectorShape | ParamRef;
43
+ /**
44
+ * The vector’s position along its orientation relative to its anchor point; a
45
+ * constant. Assuming a default **rotate** angle of 0°, one of:
46
+ *
47
+ * - *start* - from [*x*, *y*] to [*x*, *y* - *l*]
48
+ * - *middle* (default) - from [*x*, *y* + *l* / 2] to [*x*, *y* - *l* / 2]
49
+ * - *end* - from [*x*, *y* + *l*] to [*x*, *y*]
50
+ *
51
+ * where [*x*, *y*] is the vector’s anchor point and *l* is the vector’s
52
+ * (possibly scaled) length in pixels.
53
+ */
54
+ anchor?: 'start' | 'middle' | 'end' | ParamRef;
55
+ /**
56
+ * The vector’s frame anchor, to default **x** and **y** relative to the
57
+ * frame; a constant representing one of the frame corners (*top-left*,
58
+ * *top-right*, *bottom-right*, *bottom-left*), sides (*top*, *right*,
59
+ * *bottom*, *left*), or *middle* (default). Has no effect if both **x**
60
+ * and **y** are specified.
61
+ */
62
+ frameAnchor?: FrameAnchor | ParamRef;
63
+ }
64
+ /** The vector mark. */
65
+ export interface Vector extends MarkData, VectorOptions {
66
+ /**
67
+ * A vector mark.
68
+ *
69
+ * If none of **frameAnchor**, **x**, and **y** are specified, then **x** and
70
+ * **y** default to accessors assuming that *data* contains tuples [[*x₀*,
71
+ * *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …]
72
+ */
73
+ mark: 'vector';
74
+ }
75
+ /** The vectorX mark. */
76
+ export interface VectorX extends MarkData, VectorOptions {
77
+ /**
78
+ * Like vector, but **x** instead defaults to the identity function and **y**
79
+ * defaults to null, assuming that *data* is an array of numbers [*x₀*, *x₁*,
80
+ * *x₂*, …].
81
+ */
82
+ mark: 'vectorX';
83
+ }
84
+ /** The vectorY mark. */
85
+ export interface VectorY extends MarkData, VectorOptions {
86
+ /**
87
+ * Like vector, but **y** instead defaults to the identity function and **x**
88
+ * defaults to null, assuming that *data* is an array of numbers [*y₀*, *y₁*,
89
+ * *y₂*, …].
90
+ */
91
+ mark: 'vectorY';
92
+ }
93
+ /** The spike mark. */
94
+ export interface Spike extends MarkData, VectorOptions {
95
+ /**
96
+ * Like vector, but with default *options* suitable for drawing a spike map.
97
+ */
98
+ mark: 'spike';
99
+ }
@@ -0,0 +1,12 @@
1
+ export function paramRef(value: any): any;
2
+ export function paramStr(value: any): any;
3
+ export function toParamRef(name: any): string;
4
+ export function toArray(value: any): any[];
5
+ export function isArray(value: any): boolean;
6
+ export function isObject(value: any): boolean;
7
+ export function isNumber(value: any): boolean;
8
+ export function isNumberOrString(value: any): boolean;
9
+ export function isString(value: any): boolean;
10
+ export function isFunction(value: any): boolean;
11
+ export function error(message: any, data: any): void;
12
+ export function isoparse(string: any, fallback: any): any;
package/jsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "include": ["src/**/*", "../../specs/ts/*.ts"],
3
+ "compilerOptions": {
4
+ "checkJs": true,
5
+ "noEmit": true,
6
+ "noImplicitAny": false,
7
+ "module": "node16",
8
+ "skipLibCheck": true
9
+ }
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uwdata/mosaic-spec",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Declarative specification of Mosaic-powered applications.",
5
5
  "keywords": [
6
6
  "mosaic",
@@ -16,22 +16,29 @@
16
16
  "module": "src/index.js",
17
17
  "jsdelivr": "dist/mosaic-spec.min.js",
18
18
  "unpkg": "dist/mosaic-spec.min.js",
19
+ "types": "dist/types/index.d.ts",
19
20
  "repository": {
20
21
  "type": "git",
21
22
  "url": "https://github.com/uwdata/mosaic.git"
22
23
  },
23
24
  "scripts": {
24
25
  "prebuild": "rimraf dist && mkdir dist",
25
- "build": "node ../../esbuild.js mosaic-spec",
26
- "lint": "eslint src test --ext .js",
27
- "test": "mocha 'test/**/*-test.js'",
26
+ "build": "npm run types && node ../../esbuild.js mosaic-spec",
27
+ "lint": "eslint src test",
28
+ "types": "tsc -p tsconfig.json && npm run schema",
29
+ "schema": "ts-json-schema-generator -f tsconfig.json -p src/spec/Spec.ts -t Spec --no-type-check --no-ref-encode --functions hide > dist/mosaic-schema.json",
30
+ "pretest": "npm run prebuild && npm run types",
31
+ "test": "mocha 'test/**/*-test.js' && tsc -p jsconfig.json",
28
32
  "prepublishOnly": "npm run test && npm run lint && npm run build"
29
33
  },
30
34
  "dependencies": {
31
- "@uwdata/mosaic-core": "^0.7.0",
32
- "@uwdata/mosaic-sql": "^0.7.0",
33
- "@uwdata/vgplot": "^0.7.0",
34
- "isoformat": "^0.2.1"
35
+ "@uwdata/mosaic-core": "^0.8.0",
36
+ "@uwdata/mosaic-sql": "^0.8.0",
37
+ "@uwdata/vgplot": "^0.8.0",
38
+ "ts-json-schema-generator": "^2.1.1"
35
39
  },
36
- "gitHead": "4680b922f15579b7b527f31507ed71a12230ec35"
40
+ "devDependencies": {
41
+ "ajv": "^8.13.0"
42
+ },
43
+ "gitHead": "a24b4c9f7dfa1c38c6af96ec17e075326c1af9b0"
37
44
  }
@@ -1,18 +1,39 @@
1
+ /**
2
+ * Abstract base class for Mosaic spec AST nodes.
3
+ */
1
4
  export class ASTNode {
2
5
  constructor(type, children = null) {
6
+ /** @type {string} */
3
7
  this.type = type;
8
+ /** @type {ASTNode[] | null} */
4
9
  this.children = children;
5
10
  }
6
11
 
7
- instantiate(/* ctx */) {
12
+ /**
13
+ * Instantiate this AST node to use in a live web application.
14
+ * @param {import('../ast-to-dom.js').InstantiateContext} ctx The instantiation context.
15
+ * @returns {*} The instantiated value of this node.
16
+ */
17
+ instantiate(ctx) { // eslint-disable-line no-unused-vars
18
+ // @ts-ignore
8
19
  throw Error('instantiate not implemented');
9
20
  }
10
21
 
11
- codegen(/* ctx */) {
22
+ /**
23
+ * Generate ESM code for this AST node.
24
+ * @param {import('../ast-to-esm.js').CodegenContext} ctx The code generator context.
25
+ * @returns {string|void} The generated ESM code for the node.
26
+ */
27
+ codegen(ctx) { // eslint-disable-line no-unused-vars
28
+ // @ts-ignore
12
29
  return Error('codegen not implemented');
13
30
  }
14
31
 
32
+ /**
33
+ * @returns {*} This AST node in JSON specification format.
34
+ */
15
35
  toJSON() {
36
+ // @ts-ignore
16
37
  return Error('toJSON not implemented');
17
38
  }
18
39
  }
@@ -10,6 +10,7 @@ export const CSV_DATA = 'csv';
10
10
  export const JSON_DATA = 'json';
11
11
  export const SPATIAL_DATA = 'spatial';
12
12
 
13
+ // @ts-ignore
13
14
  const dataFormats = new Map([
14
15
  [TABLE_DATA, parseTableData],
15
16
  [PARQUET_DATA, parseParquetData],
@@ -18,16 +19,40 @@ const dataFormats = new Map([
18
19
  [SPATIAL_DATA, parseSpatialData]
19
20
  ]);
20
21
 
22
+ /**
23
+ * Parse a data definition spec.
24
+ * @param {string} name The name of the dataset
25
+ * @param {import('../spec/Data.js').DataDefinition} spec The data definition spec.
26
+ * @param {import('../parse-spec.js').ParseContext} ctx The parser context.
27
+ * @returns {DataNode} a parsed data definition AST node
28
+ */
21
29
  export function parseData(name, spec, ctx) {
22
- spec = resolveDataSpec(spec);
23
- if (dataFormats.has(spec.type)) {
24
- const parse = dataFormats.get(spec.type);
25
- return parse(name, spec, ctx);
30
+ const def = resolveDataSpec(spec);
31
+ if (dataFormats.has(def.type)) {
32
+ const parse = dataFormats.get(def.type);
33
+ return parse(name, def, ctx);
26
34
  } else {
27
35
  ctx.error(`Unrecognized data format type.`, spec);
28
36
  }
29
37
  }
30
38
 
39
+ function resolveDataSpec(spec) {
40
+ if (isArray(spec)) spec = { type: 'json', data: spec };
41
+ if (isString(spec)) spec = { type: 'table', query: spec };
42
+ return { ...spec, type: inferType(spec) };
43
+ }
44
+
45
+ function inferType(spec) {
46
+ return spec.type
47
+ || fileExtension(spec.file)
48
+ || 'table';
49
+ }
50
+
51
+ function fileExtension(file) {
52
+ const idx = file?.lastIndexOf('.');
53
+ return idx > 0 ? file.slice(idx + 1) : null;
54
+ }
55
+
31
56
  function parseTableData(name, spec, ctx) {
32
57
  // eslint-disable-next-line no-unused-vars
33
58
  const { query, type, ...options } = spec;
@@ -61,23 +86,6 @@ function parseSpatialData(name, spec, ctx) {
61
86
  return new SpatialDataNode(name, file, parseOptions(options, ctx));
62
87
  }
63
88
 
64
- function resolveDataSpec(spec) {
65
- if (isArray(spec)) spec = { type: 'json', data: spec };
66
- if (isString(spec)) spec = { type: 'table', query: spec };
67
- return { ...spec, type: inferType(spec) };
68
- }
69
-
70
- function inferType(spec) {
71
- return spec.type
72
- || fileExtension(spec.file)
73
- || 'table';
74
- }
75
-
76
- function fileExtension(file) {
77
- const idx = file?.lastIndexOf('.');
78
- return idx > 0 ? file.slice(idx + 1) : null;
79
- }
80
-
81
89
  function resolveFileURL(file, baseURL) {
82
90
  return baseURL ? new URL(file, baseURL).toString() : file;
83
91
  }
@@ -100,19 +108,39 @@ export class QueryDataNode extends DataNode {
100
108
  super(name, format);
101
109
  }
102
110
 
111
+ /**
112
+ * Instantiate a table creation query.
113
+ * @param {import('../ast-to-dom.js').InstantiateContext} ctx The instantiation context.
114
+ * @returns {string|void} The instantiated query.
115
+ */
103
116
  instantiateQuery(ctx) {
104
117
  ctx.error('instantiateQuery not implemented');
105
118
  }
106
119
 
120
+ /**
121
+ * Code generate a table creation query.
122
+ * @param {import('../ast-to-esm.js').CodegenContext} ctx The code generator context.
123
+ * @returns {string|void} The generated query code.
124
+ */
107
125
  codegenQuery(ctx) {
108
126
  ctx.error('codegenQuery not implemented');
109
127
  }
110
128
 
129
+ /**
130
+ * Instantiate this AST node to use in a live web application.
131
+ * @param {import('../ast-to-dom.js').InstantiateContext} ctx The instantiation context.
132
+ * @returns {*} The instantiated value of this node.
133
+ */
111
134
  instantiate(ctx) {
112
135
  const query = this.instantiateQuery(ctx);
113
136
  if (query) return query;
114
137
  }
115
138
 
139
+ /**
140
+ * Generate ESM code for this AST node.
141
+ * @param {import('../ast-to-esm.js').CodegenContext} ctx The code generator context.
142
+ * @returns {string|void} The generated ESM code for the node.
143
+ */
116
144
  codegen(ctx) {
117
145
  const query = this.codegenQuery(ctx);
118
146
  if (query) return query;
@@ -126,6 +154,11 @@ export class TableDataNode extends QueryDataNode {
126
154
  this.options = options;
127
155
  }
128
156
 
157
+ /**
158
+ * Instantiate a table creation query.
159
+ * @param {import('../ast-to-dom.js').InstantiateContext} ctx The instantiation context.
160
+ * @returns {string|void} The instantiated query.
161
+ */
129
162
  instantiateQuery(ctx) {
130
163
  const { name, query, options } = this;
131
164
  if (query) {
@@ -133,6 +166,11 @@ export class TableDataNode extends QueryDataNode {
133
166
  }
134
167
  }
135
168
 
169
+ /**
170
+ * Code generate a table creation query.
171
+ * @param {import('../ast-to-esm.js').CodegenContext} ctx The code generator context.
172
+ * @returns {string|void} The generated query code.
173
+ */
136
174
  codegenQuery(ctx) {
137
175
  const { name, query, options } = this;
138
176
  if (query) {
@@ -154,6 +192,11 @@ export class FileDataNode extends QueryDataNode {
154
192
  this.options = options;
155
193
  }
156
194
 
195
+ /**
196
+ * Instantiate a table creation query.
197
+ * @param {import('../ast-to-dom.js').InstantiateContext} ctx The instantiation context.
198
+ * @returns {string|void} The instantiated query.
199
+ */
157
200
  instantiateQuery(ctx) {
158
201
  const { name, method, file, options } = this;
159
202
  const url = resolveFileURL(file, ctx.baseURL);
@@ -161,6 +204,11 @@ export class FileDataNode extends QueryDataNode {
161
204
  return ctx.api[method](name, url, opt);
162
205
  }
163
206
 
207
+ /**
208
+ * Code generate a table creation query.
209
+ * @param {import('../ast-to-esm.js').CodegenContext} ctx The code generator context.
210
+ * @returns {string|void} The generated query code.
211
+ */
164
212
  codegenQuery(ctx) {
165
213
  const { name, method, file, options } = this;
166
214
  const url = resolveFileURL(file, ctx.baseURL);
@@ -205,11 +253,21 @@ export class LiteralJSONDataNode extends QueryDataNode {
205
253
  this.options = options;
206
254
  }
207
255
 
256
+ /**
257
+ * Instantiate a table creation query.
258
+ * @param {import('../ast-to-dom.js').InstantiateContext} ctx The instantiation context.
259
+ * @returns {string|void} The instantiated query.
260
+ */
208
261
  instantiateQuery(ctx) {
209
262
  const { name, data, options } = this;
210
263
  return ctx.api.loadObjects(name, data, options.instantiate(ctx));
211
264
  }
212
265
 
266
+ /**
267
+ * Code generate a table creation query.
268
+ * @param {import('../ast-to-esm.js').CodegenContext} ctx The code generator context.
269
+ * @returns {string|void} The generated query code.
270
+ */
213
271
  codegenQuery(ctx) {
214
272
  const { name, data, options } = this;
215
273
  const opt = options ? ',' + options.codegen(ctx) : '';
@@ -16,7 +16,7 @@ export class HSpaceNode extends ASTNode {
16
16
  }
17
17
 
18
18
  codegen(ctx) {
19
- return `${ctx.tab()}${ctx.ns()}${this.type}(${this.value})`;
19
+ return `${ctx.tab()}${ctx.ns()}${this.type}(${ctx.stringify(this.value)})`;
20
20
  }
21
21
 
22
22
  toJSON() {
@@ -18,8 +18,7 @@ export class InputNode extends ASTNode {
18
18
  }
19
19
 
20
20
  instantiate(ctx) {
21
- const fn = ctx.api[this.name];
22
- return fn(this.options.instantiate(ctx));
21
+ return ctx.api[this.name](this.options.instantiate(ctx));
23
22
  }
24
23
 
25
24
  codegen(ctx) {
@@ -1,5 +1,4 @@
1
- import { parse as isoparse } from 'isoformat';
2
- import { isArray, isObject } from '../util.js';
1
+ import { isArray, isObject, isoparse } from '../util.js';
3
2
  import { ASTNode } from './ASTNode.js';
4
3
  import { CROSSFILTER, INTERSECT, PARAM, SINGLE, UNION, VALUE } from '../constants.js';
5
4
  import { SelectionNode } from './SelectionNode.js';
@@ -20,8 +20,7 @@ export class PlotAttributeNode extends ASTNode {
20
20
 
21
21
  instantiate(ctx) {
22
22
  const { name, value } = this;
23
- const fn = ctx.api[name];
24
- return fn(value.instantiate(ctx));
23
+ return ctx.api[name](value.instantiate(ctx));
25
24
  }
26
25
 
27
26
  codegen(ctx) {
@@ -18,8 +18,7 @@ export class PlotInteractorNode extends ASTNode {
18
18
  }
19
19
 
20
20
  instantiate(ctx) {
21
- const fn = ctx.api[this.name];
22
- return fn(this.options.instantiate(ctx));
21
+ return ctx.api[this.name](this.options.instantiate(ctx));
23
22
  }
24
23
 
25
24
  codegen(ctx) {
@@ -20,8 +20,7 @@ export class PlotLegendNode extends ASTNode {
20
20
  }
21
21
 
22
22
  instantiate(ctx) {
23
- const fn = ctx.api[this.key];
24
- return fn(this.options.instantiate(ctx));
23
+ return ctx.api[this.key](this.options.instantiate(ctx));
25
24
  }
26
25
 
27
26
  codegen(ctx) {
@@ -41,9 +41,10 @@ export class PlotMarkNode extends ASTNode {
41
41
 
42
42
  instantiate(ctx) {
43
43
  const { name, data, options } = this;
44
- const fn = ctx.api[name];
45
44
  const opt = options.instantiate(ctx);
46
- return data ? fn(data.instantiate(ctx), opt) : fn(opt);
45
+ return data
46
+ ? ctx.api[name](data.instantiate(ctx), opt)
47
+ : ctx.api[name](opt);
47
48
  }
48
49
 
49
50
  codegen(ctx) {
@@ -41,8 +41,7 @@ export class TransformNode extends ASTNode {
41
41
  const { name, args, options } = this;
42
42
  const { distinct, orderby, partitionby, rows, range } = options;
43
43
 
44
- const fn = ctx.api[name];
45
- let expr = fn(...args);
44
+ let expr = ctx.api[name](...args);
46
45
  if (distinct) {
47
46
  expr = expr.distinct();
48
47
  }
@@ -16,7 +16,7 @@ export class VSpaceNode extends ASTNode {
16
16
  }
17
17
 
18
18
  codegen(ctx) {
19
- return `${ctx.tab()}${ctx.ns()}${this.type}(${this.value})`;
19
+ return `${ctx.tab()}${ctx.ns()}${this.type}(${ctx.stringify(this.value)})`;
20
20
  }
21
21
 
22
22
  toJSON() {
package/src/ast-to-dom.js CHANGED
@@ -1,15 +1,21 @@
1
+ import { Param, Selection } from '@uwdata/mosaic-core';
1
2
  import { createAPIContext, loadExtension } from '@uwdata/vgplot';
2
3
  import { SpecNode } from './ast/SpecNode.js';
3
4
  import { resolveExtensions } from './config/extensions.js';
4
5
  import { error } from './util.js';
5
6
 
6
7
  /**
7
- * Generate running web application (DOM content) for a Mosaic spec AST.
8
+ * Generate a running web application (DOM content) for a Mosaic spec AST.
8
9
  * @param {SpecNode} ast Mosaic AST root node.
9
10
  * @param {object} [options] Instantiation options.
10
11
  * @param {string} [options.baseURL] The base URL for loading data files.
11
- * @returns {object} An object with the resulting DOM element, and
12
- * a map of named parameters (Param and Selection instances).
12
+ * @param {any[]} [options.plotDefaults] Array of default plot attributes.
13
+ * @param {Map<string, Param>} [options.params] A map of predefined Params/Selections.
14
+ * @returns {Promise<{
15
+ * element: HTMLElement | SVGSVGElement;
16
+ * params: Map<string, Param | Selection>;
17
+ * }>} A Promise to an object with the resulting
18
+ * DOM element, and a map of named parameters (Param and Selection instances).
13
19
  */
14
20
  export async function astToDOM(ast, options) {
15
21
  const { data, params, plotDefaults } = ast;
@@ -33,9 +39,12 @@ export async function astToDOM(ast, options) {
33
39
  }
34
40
 
35
41
  // process param/selection definitions
42
+ // skip definitions with names already defined
36
43
  for (const [name, node] of Object.entries(params)) {
37
- const param = node.instantiate(ctx);
38
- ctx.activeParams.set(name, param);
44
+ if (!ctx.activeParams.has(name)) {
45
+ const param = node.instantiate(ctx);
46
+ ctx.activeParams.set(name, param);
47
+ }
39
48
  }
40
49
 
41
50
  return {
@@ -48,12 +57,12 @@ export class InstantiateContext {
48
57
  constructor({
49
58
  api = createAPIContext(),
50
59
  plotDefaults = [],
51
- activeParams = new Map,
60
+ params = new Map,
52
61
  baseURL = null
53
62
  } = {}) {
54
63
  this.api = api;
55
64
  this.plotDefaults = plotDefaults;
56
- this.activeParams = activeParams;
65
+ this.activeParams = params;
57
66
  this.baseURL = baseURL;
58
67
  this.coordinator = api.context.coordinator;
59
68
  }
package/src/ast-to-esm.js CHANGED
@@ -1,22 +1,27 @@
1
1
  import { SpecNode } from './ast/SpecNode.js';
2
2
  import { resolveExtensions } from './config/extensions.js';
3
- import { error, isArray, isObject, isString, toParamRef } from './util.js';
3
+ import { error, isArray, isObject, isString, toArray, toParamRef } from './util.js';
4
4
 
5
5
  /**
6
6
  * Generate ESM code for a Mosaic spec AST.
7
7
  * @param {SpecNode} ast Mosaic AST root node.
8
8
  * @param {object} [options] Code generation options.
9
- * @param {string} [options.namespace='vg'] The vgplot API namespace object.
10
9
  * @param {string} [options.baseURL] The base URL for loading data files.
10
+ * @param {string} [options.connector] A database connector to initialize.
11
+ * Valid values are 'wasm', 'socket', and 'rest'.
12
+ * If undefined, no connector code is generated.
13
+ * @param {string} [options.namespace='vg'] The vgplot API namespace object.
11
14
  * @param {number} [options.depth=0] The starting indentation depth.
12
- * @param {Map<string,string>} [options.imports] A Map of ESM imports to
13
- * include in generated code. Keys are packages (e.g., '@uwdata/vgplot')
14
- * and values indicate what to import (e.g., '* as vg').
15
+ * @param {Map<string,string|string[]>} [options.imports] A Map of ESM
16
+ * imports to include in generated code. Keys are packages (e.g.,
17
+ * '@uwdata/vgplot') and values indicate what to import (e.g., '* as vg').
18
+ * @param {string|string[]} [options.preamble] Code to include after imports.
15
19
  * @returns {string} Generated ESM code using the vgplot API.
16
20
  */
17
- export function astToESM(ast, options) {
21
+ export function astToESM(ast, options = {}) {
18
22
  const { root, data, params, plotDefaults } = ast;
19
- const ctx = new CodegenContext({ plotDefaults, ...options });
23
+ const { preamble, ...ctxOptions } = options;
24
+ const ctx = new CodegenContext({ plotDefaults, ...ctxOptions });
20
25
 
21
26
  // generate package imports
22
27
  const importsCode = [];
@@ -24,10 +29,16 @@ export function astToESM(ast, options) {
24
29
  importsCode.push(
25
30
  isString(methods)
26
31
  ? `import ${methods} from "${pkg}";`
32
+ // @ts-ignore
27
33
  : `import { ${methods.join(', ')} } from "${pkg}";`
28
34
  );
29
35
  }
30
36
 
37
+ // preamble code comes directly after imports
38
+ const preambleCode = preamble
39
+ ? toArray(preamble).map(s => `${ctx.tab()}${s}`)
40
+ : [];
41
+
31
42
  // generate database connector code
32
43
  const connectorCode = [];
33
44
  if (ctx.connector) {
@@ -82,6 +93,8 @@ export function astToESM(ast, options) {
82
93
  return [
83
94
  ...importsCode,
84
95
  ...maybeNewline(importsCode),
96
+ ...preambleCode,
97
+ ...maybeNewline(preambleCode),
85
98
  ...connectorCode,
86
99
  ...maybeNewline(connectorCode),
87
100
  ...dataCode,
@@ -95,12 +108,26 @@ export function astToESM(ast, options) {
95
108
  }
96
109
 
97
110
  export class CodegenContext {
111
+ /**
112
+ * Create a new code generator context.
113
+ * @param {object} [options] Code generation options.
114
+ * @param {*} [options.plotDefaults] Default attributes to apply to all plots.
115
+ * @param {string} [options.baseURL] The base URL for loading data files.
116
+ * @param {string} [options.connector] A database connector to initialize.
117
+ * Valid values are 'wasm', 'socket', and 'rest'.
118
+ * If undefined, no connector code is generated.
119
+ * @param {string} [options.namespace='vg'] The vgplot API namespace object.
120
+ * @param {number} [options.depth=0] The starting indentation depth.
121
+ * @param {Map<string,string|string[]>} [options.imports] A Map of ESM
122
+ * imports to include in generated code. Keys are packages (e.g.,
123
+ * '@uwdata/vgplot') and values indicate what to import (e.g., '* as vg').
124
+ */
98
125
  constructor({
99
- plotDefaults = null,
126
+ plotDefaults = undefined,
100
127
  namespace = 'vg',
101
- connector = null,
128
+ connector = undefined,
102
129
  imports = new Map([['@uwdata/vgplot', '* as vg']]),
103
- baseURL = null,
130
+ baseURL = undefined,
104
131
  depth = 0
105
132
  } = {}) {
106
133
  this.plotDefaults = plotDefaults;
@@ -113,9 +140,11 @@ export class CodegenContext {
113
140
 
114
141
  addImport(pkg, method) {
115
142
  if (!this.imports.has(pkg)) {
116
- this.imports.set(pkg, []);
143
+ this.imports.set(pkg, [method]);
144
+ } else {
145
+ // @ts-ignore
146
+ this.imports.get(pkg).push(method);
117
147
  }
118
- this.imports.get(pkg).push(method);
119
148
  }
120
149
 
121
150
  setImports(pkg, all) {
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * Set of input widget type names.
3
+ * @returns {Set<string>}
3
4
  */
4
5
  export function inputNames(overrides = []) {
5
6
  return new Set([