@octaviaflow/scss-generator 1.0.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.
- package/package.json +24 -0
- package/src/generate/index.js +28 -0
- package/src/generate/printer.js +81 -0
- package/src/index.js +20 -0
- package/src/types/assert.js +89 -0
- package/src/types/definitions.js +831 -0
- package/src/types/index.js +26 -0
- package/src/types/type.js +80 -0
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@octaviaflow/scss-generator",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/octaviaflow-design-system.git",
|
|
9
|
+
"directory": "packages/scss-generator"
|
|
10
|
+
},
|
|
11
|
+
"bugs": "https://github.com/octaviaflow-design-system/issues",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"octaviaflow",
|
|
14
|
+
"octaviaflow-design-system",
|
|
15
|
+
"scss-generator",
|
|
16
|
+
"react"
|
|
17
|
+
],
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"prettier": "^3.3.3"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright OctaviaFlow
|
|
3
|
+
* Author: Vishal Kumar
|
|
4
|
+
* Created: 11/November/2025
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const { definitions } = require('../types');
|
|
14
|
+
const { createPrinter } = require('./printer');
|
|
15
|
+
|
|
16
|
+
function generate(ast) {
|
|
17
|
+
const printer = createPrinter(definitions);
|
|
18
|
+
|
|
19
|
+
printer.print(ast);
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
code: printer.get(),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
generate,
|
|
28
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright OctaviaFlow
|
|
3
|
+
* Author: Vishal Kumar
|
|
4
|
+
* Created: 11/November/2025
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const prettier = require('prettier2');
|
|
14
|
+
|
|
15
|
+
const prettierOptions = {
|
|
16
|
+
parser: 'scss',
|
|
17
|
+
printWidth: 80,
|
|
18
|
+
singleQuote: true,
|
|
19
|
+
trailingComma: 'es5',
|
|
20
|
+
proseWrap: 'always',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function createPrinter(definitions) {
|
|
24
|
+
let buffer = [];
|
|
25
|
+
let indentLevel = 0;
|
|
26
|
+
|
|
27
|
+
const printer = {
|
|
28
|
+
append(string) {
|
|
29
|
+
buffer.push(string);
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
blockStart(character = '{') {
|
|
33
|
+
printer.token(character);
|
|
34
|
+
indentLevel++;
|
|
35
|
+
printer.newline();
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
blockEnd(character = '}') {
|
|
39
|
+
indentLevel--;
|
|
40
|
+
printer.newline();
|
|
41
|
+
printer.token(character);
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
get() {
|
|
45
|
+
return prettier.format(buffer.join(''), prettierOptions);
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
maybeNewline() {
|
|
49
|
+
if (buffer[buffer.length - 1] !== '\n') {
|
|
50
|
+
printer.newline();
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
newline() {
|
|
55
|
+
buffer.push('\n');
|
|
56
|
+
buffer.push(padLeft(indentLevel));
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
print(node, parent) {
|
|
60
|
+
definitions[node.type].generate(printer, node, parent);
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
space() {
|
|
64
|
+
buffer.push(' ');
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
token(characters) {
|
|
68
|
+
return buffer.push(characters);
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return printer;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function padLeft(level) {
|
|
76
|
+
return ' '.repeat(level);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = {
|
|
80
|
+
createPrinter,
|
|
81
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright OctaviaFlow
|
|
3
|
+
* Author: Vishal Kumar
|
|
4
|
+
* Created: 11/November/2025
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const { definitions, types } = require('./types');
|
|
14
|
+
const { generate } = require('./generate');
|
|
15
|
+
|
|
16
|
+
module.exports = {
|
|
17
|
+
definitions,
|
|
18
|
+
generate,
|
|
19
|
+
types,
|
|
20
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright OctaviaFlow
|
|
3
|
+
* Author: Vishal Kumar
|
|
4
|
+
* Created: 11/November/2025
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
function noop() {}
|
|
14
|
+
|
|
15
|
+
function assertAny() {
|
|
16
|
+
return noop;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function assertDefined(node) {
|
|
20
|
+
if (!node) {
|
|
21
|
+
throw new Error(`Expected node of type ${node.type} to be defined`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function assertValueType(expected) {
|
|
26
|
+
return (value) => {
|
|
27
|
+
if (typeof value !== expected) {
|
|
28
|
+
throw new TypeError(
|
|
29
|
+
`Expected value to be of type ${expected}, instead ` +
|
|
30
|
+
`received ${typeof value}`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function assertType({ type }) {
|
|
37
|
+
return (node) => {
|
|
38
|
+
assertDefined(node);
|
|
39
|
+
|
|
40
|
+
if (node.type !== type) {
|
|
41
|
+
throw new TypeError(
|
|
42
|
+
`Expected node to be of type ${type}, instead received: ` +
|
|
43
|
+
`${node.type}`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function assertOneOf(types) {
|
|
50
|
+
return (value, node) => {
|
|
51
|
+
const errors = [];
|
|
52
|
+
for (let i = 0; i < types.length; i++) {
|
|
53
|
+
try {
|
|
54
|
+
types[i](value);
|
|
55
|
+
return;
|
|
56
|
+
} catch (error) {
|
|
57
|
+
// Including this in case we have a program error instead of a TypeError
|
|
58
|
+
if (!(error instanceof TypeError)) {
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
errors.push(error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
throw new TypeError(
|
|
67
|
+
`Expected node to match one of the expected types for ${node.type}.\n\n` +
|
|
68
|
+
errors.map((error) => error.message).join('\n') +
|
|
69
|
+
'\n'
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function arrayOf(checkType) {
|
|
75
|
+
return (nodes = [], node) => {
|
|
76
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
77
|
+
checkType(nodes[i], node);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = {
|
|
83
|
+
assertAny,
|
|
84
|
+
assertDefined,
|
|
85
|
+
assertOneOf,
|
|
86
|
+
assertType,
|
|
87
|
+
assertValueType,
|
|
88
|
+
arrayOf,
|
|
89
|
+
};
|
|
@@ -0,0 +1,831 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright OctaviaFlow
|
|
3
|
+
* Author: Vishal Kumar
|
|
4
|
+
* Created: 11/November/2025
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
assertAny,
|
|
15
|
+
assertOneOf,
|
|
16
|
+
assertType,
|
|
17
|
+
assertValueType,
|
|
18
|
+
arrayOf,
|
|
19
|
+
} = require('./assert');
|
|
20
|
+
const { defineType } = require('./type');
|
|
21
|
+
|
|
22
|
+
//-------------------------------------------------------------------------------
|
|
23
|
+
// Comments
|
|
24
|
+
//-------------------------------------------------------------------------------
|
|
25
|
+
const Comment = defineType('Comment', {
|
|
26
|
+
fields: {
|
|
27
|
+
value: {
|
|
28
|
+
validate: assertValueType('string'),
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
generate(printer, node) {
|
|
32
|
+
const lines = node.value.split('\n');
|
|
33
|
+
for (let i = 0; i < lines.length; i++) {
|
|
34
|
+
printer.token('//');
|
|
35
|
+
printer.token(lines[i]);
|
|
36
|
+
if (i !== lines.length - 1) {
|
|
37
|
+
printer.newline();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
//-------------------------------------------------------------------------------
|
|
44
|
+
// Identifier
|
|
45
|
+
//-------------------------------------------------------------------------------
|
|
46
|
+
const Identifier = defineType('Identifier', {
|
|
47
|
+
fields: {
|
|
48
|
+
name: {
|
|
49
|
+
validate: assertValueType('string'),
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
generate(printer, node, parent) {
|
|
53
|
+
if (
|
|
54
|
+
parent &&
|
|
55
|
+
(parent.type === Assignment.type ||
|
|
56
|
+
parent.type === AssignmentPattern.type ||
|
|
57
|
+
parent.type === CallExpression.type ||
|
|
58
|
+
parent.type === LogicalExpression.type ||
|
|
59
|
+
parent.type === SassMixin.type ||
|
|
60
|
+
parent.type === SassList.type ||
|
|
61
|
+
parent.type === RestPattern.type ||
|
|
62
|
+
parent.type === SassFunction.type)
|
|
63
|
+
) {
|
|
64
|
+
printer.token('$');
|
|
65
|
+
}
|
|
66
|
+
printer.token(node.name);
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
//-------------------------------------------------------------------------------
|
|
71
|
+
// Blocks
|
|
72
|
+
//-------------------------------------------------------------------------------
|
|
73
|
+
const BlockStatement = defineType('BlockStatement', {
|
|
74
|
+
fields: {
|
|
75
|
+
body: {
|
|
76
|
+
validate: arrayOf(assertAny),
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
generate(printer, node) {
|
|
80
|
+
printer.blockStart();
|
|
81
|
+
for (let i = 0; i < node.body.length; i++) {
|
|
82
|
+
printer.print(node.body[i], node);
|
|
83
|
+
if (i !== node.body.length - 1) {
|
|
84
|
+
printer.newline();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
printer.blockEnd();
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
//-------------------------------------------------------------------------------
|
|
92
|
+
// Values
|
|
93
|
+
//-------------------------------------------------------------------------------
|
|
94
|
+
const SassBoolean = defineType('SassBoolean', {
|
|
95
|
+
fields: {
|
|
96
|
+
value: {
|
|
97
|
+
validate: assertValueType('boolean'),
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
generate(printer, node) {
|
|
101
|
+
printer.token(node.value);
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const SassColor = defineType('SassColor', {
|
|
106
|
+
fields: {
|
|
107
|
+
value: {
|
|
108
|
+
validate: assertValueType('string'),
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
generate(printer, node) {
|
|
112
|
+
printer.token(node.value);
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const SassFunction = defineType('SassFunction', {
|
|
117
|
+
fields: {
|
|
118
|
+
id: {
|
|
119
|
+
validate: assertType(Identifier),
|
|
120
|
+
},
|
|
121
|
+
params: {
|
|
122
|
+
optional: true,
|
|
123
|
+
validate: () =>
|
|
124
|
+
arrayOf(
|
|
125
|
+
assertOneOf([assertType(AssignmentPattern), assertType(Identifier)])
|
|
126
|
+
),
|
|
127
|
+
},
|
|
128
|
+
body: {
|
|
129
|
+
validate: () => assertType(BlockStatement),
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
generate(printer, node, parent) {
|
|
133
|
+
printer.token('@function');
|
|
134
|
+
printer.space();
|
|
135
|
+
printer.print(node.id, parent);
|
|
136
|
+
printer.token('(');
|
|
137
|
+
|
|
138
|
+
if (Array.isArray(node.params)) {
|
|
139
|
+
for (let i = 0; i < node.params.length; i++) {
|
|
140
|
+
printer.print(node.params[i], node);
|
|
141
|
+
if (i !== node.params.length - 1) {
|
|
142
|
+
printer.token(',');
|
|
143
|
+
printer.space();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
printer.token(')');
|
|
149
|
+
printer.space();
|
|
150
|
+
printer.print(node.body, parent);
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const SassList = defineType('SassList', {
|
|
155
|
+
fields: {
|
|
156
|
+
elements: {
|
|
157
|
+
validate: () =>
|
|
158
|
+
arrayOf(
|
|
159
|
+
assertOneOf([
|
|
160
|
+
assertType(SassBoolean),
|
|
161
|
+
assertType(SassList),
|
|
162
|
+
assertType(SassMap),
|
|
163
|
+
assertType(SassNumber),
|
|
164
|
+
assertType(SassString),
|
|
165
|
+
assertType(Identifier),
|
|
166
|
+
])
|
|
167
|
+
),
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
generate(printer, node) {
|
|
171
|
+
printer.token('(');
|
|
172
|
+
for (let i = 0; i < node.elements.length; i++) {
|
|
173
|
+
printer.print(node.elements[i], node);
|
|
174
|
+
if (i !== node.elements.length - 1) {
|
|
175
|
+
printer.token(',');
|
|
176
|
+
printer.space();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
printer.token(')');
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const SassMap = defineType('SassMap', {
|
|
184
|
+
fields: {
|
|
185
|
+
properties: {
|
|
186
|
+
validate: () => arrayOf(assertType(SassMapProperty)),
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
generate(printer, node) {
|
|
190
|
+
printer.blockStart('(');
|
|
191
|
+
for (let i = 0; i < node.properties.length; i++) {
|
|
192
|
+
printer.print(node.properties[i]);
|
|
193
|
+
if (i !== node.properties.length - 1) {
|
|
194
|
+
printer.token(',');
|
|
195
|
+
printer.newline();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
printer.blockEnd(')');
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const SassMapProperty = defineType('SassMapProperty', {
|
|
203
|
+
fields: {
|
|
204
|
+
key: {
|
|
205
|
+
validate: assertType(Identifier),
|
|
206
|
+
},
|
|
207
|
+
value: {
|
|
208
|
+
validate: () =>
|
|
209
|
+
assertOneOf([SassBoolean, SassNumber, SassString, SassList, SassMap]),
|
|
210
|
+
},
|
|
211
|
+
quoted: {
|
|
212
|
+
optional: true,
|
|
213
|
+
validate: assertValueType('boolean'),
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
generate(printer, node) {
|
|
217
|
+
if (node.quoted) {
|
|
218
|
+
printer.token(`'`);
|
|
219
|
+
printer.print(node.key, node);
|
|
220
|
+
printer.token(`'`);
|
|
221
|
+
} else {
|
|
222
|
+
printer.print(node.key, node);
|
|
223
|
+
}
|
|
224
|
+
printer.token(':');
|
|
225
|
+
printer.space();
|
|
226
|
+
printer.print(node.value, node);
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const SassMixin = defineType('SassMixin', {
|
|
231
|
+
fields: {
|
|
232
|
+
id: {
|
|
233
|
+
validate: assertType(Identifier),
|
|
234
|
+
},
|
|
235
|
+
params: {
|
|
236
|
+
optional: true,
|
|
237
|
+
validate: () =>
|
|
238
|
+
arrayOf(
|
|
239
|
+
assertOneOf([assertType(AssignmentPattern), assertType(Identifier)])
|
|
240
|
+
),
|
|
241
|
+
},
|
|
242
|
+
body: {
|
|
243
|
+
validate: assertType(BlockStatement),
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
generate(printer, node, parent) {
|
|
247
|
+
printer.token('@mixin');
|
|
248
|
+
printer.space();
|
|
249
|
+
printer.print(node.id, parent);
|
|
250
|
+
printer.token('(');
|
|
251
|
+
|
|
252
|
+
if (Array.isArray(node.params)) {
|
|
253
|
+
for (let i = 0; i < node.params.length; i++) {
|
|
254
|
+
printer.print(node.params[i], node);
|
|
255
|
+
if (i !== node.params.length - 1) {
|
|
256
|
+
printer.token(',');
|
|
257
|
+
printer.space();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
printer.token(')');
|
|
263
|
+
printer.space();
|
|
264
|
+
printer.print(node.body, parent);
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
const SassNumber = defineType('SassNumber', {
|
|
269
|
+
fields: {
|
|
270
|
+
value: {
|
|
271
|
+
validate: assertValueType('number'),
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
generate(printer, node) {
|
|
275
|
+
printer.token(node.value);
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const SassString = defineType('SassString', {
|
|
280
|
+
fields: {
|
|
281
|
+
value: {
|
|
282
|
+
validate: assertValueType('string'),
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
generate(printer, node) {
|
|
286
|
+
printer.token(`'${node.value}'`);
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Allow ability to shortcircuit AST builder limitations and embed raw values
|
|
291
|
+
// into the Sass source code
|
|
292
|
+
const SassValue = defineType('SassValue', {
|
|
293
|
+
fields: {
|
|
294
|
+
value: {
|
|
295
|
+
validate: assertAny,
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
generate(printer, node) {
|
|
299
|
+
printer.token(node.value);
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
//-------------------------------------------------------------------------------
|
|
304
|
+
// Calls
|
|
305
|
+
//-------------------------------------------------------------------------------
|
|
306
|
+
const SassFunctionCall = defineType('SassFunctionCall', {
|
|
307
|
+
fields: {
|
|
308
|
+
id: {
|
|
309
|
+
validate: assertType(Identifier),
|
|
310
|
+
},
|
|
311
|
+
params: {
|
|
312
|
+
optional: true,
|
|
313
|
+
validate: () =>
|
|
314
|
+
arrayOf(
|
|
315
|
+
assertOneOf([
|
|
316
|
+
assertType(Identifier),
|
|
317
|
+
assertType(SassBoolean),
|
|
318
|
+
assertType(SassList),
|
|
319
|
+
assertType(SassMap),
|
|
320
|
+
assertType(SassNumber),
|
|
321
|
+
assertType(SassString),
|
|
322
|
+
assertType(SassValue),
|
|
323
|
+
])
|
|
324
|
+
),
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
generate(printer, node) {
|
|
328
|
+
printer.space();
|
|
329
|
+
printer.print(node.id);
|
|
330
|
+
printer.token('(');
|
|
331
|
+
if (Array.isArray(node.params)) {
|
|
332
|
+
for (let i = 0; i < node.params.length; i++) {
|
|
333
|
+
const param = node.params[i];
|
|
334
|
+
if (param.type === Identifier.type) {
|
|
335
|
+
printer.token('$');
|
|
336
|
+
}
|
|
337
|
+
printer.print(param, node);
|
|
338
|
+
if (i !== node.params.length - 1) {
|
|
339
|
+
printer.token(',');
|
|
340
|
+
printer.space();
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
printer.token(')');
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const SassMixinCall = defineType('SassMixinCall', {
|
|
349
|
+
fields: {
|
|
350
|
+
id: {
|
|
351
|
+
validate: assertType(Identifier),
|
|
352
|
+
},
|
|
353
|
+
params: {
|
|
354
|
+
optional: true,
|
|
355
|
+
validate: () =>
|
|
356
|
+
arrayOf(
|
|
357
|
+
assertOneOf([
|
|
358
|
+
assertType(Identifier),
|
|
359
|
+
assertType(SassBoolean),
|
|
360
|
+
assertType(SassList),
|
|
361
|
+
assertType(SassMap),
|
|
362
|
+
assertType(SassNumber),
|
|
363
|
+
assertType(SassString),
|
|
364
|
+
])
|
|
365
|
+
),
|
|
366
|
+
},
|
|
367
|
+
body: {
|
|
368
|
+
optional: true,
|
|
369
|
+
validate: assertType(BlockStatement),
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
generate(printer, node) {
|
|
373
|
+
printer.token('@include');
|
|
374
|
+
printer.space();
|
|
375
|
+
printer.print(node.id);
|
|
376
|
+
|
|
377
|
+
printer.token('(');
|
|
378
|
+
if (Array.isArray(node.params)) {
|
|
379
|
+
for (let i = 0; i < node.params.length; i++) {
|
|
380
|
+
const param = node.params[i];
|
|
381
|
+
|
|
382
|
+
if (param.type === Identifier.type) {
|
|
383
|
+
printer.token('$');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
printer.print(param, node);
|
|
387
|
+
if (i !== node.params.length - 1) {
|
|
388
|
+
printer.token(',');
|
|
389
|
+
printer.space();
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
printer.token(')');
|
|
394
|
+
|
|
395
|
+
if (node.body) {
|
|
396
|
+
printer.print(node.body, node);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
printer.token(';');
|
|
400
|
+
},
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
//-------------------------------------------------------------------------------
|
|
404
|
+
// Rules
|
|
405
|
+
//-------------------------------------------------------------------------------
|
|
406
|
+
const Rule = defineType('Rule', {
|
|
407
|
+
fields: {
|
|
408
|
+
declarations: {
|
|
409
|
+
validate: () => arrayOf(assertType(Declaration)),
|
|
410
|
+
},
|
|
411
|
+
selectors: {
|
|
412
|
+
validate: arrayOf(assertValueType('string')),
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
generate(printer, node) {
|
|
416
|
+
printer.token(node.selectors.join(', '));
|
|
417
|
+
printer.space();
|
|
418
|
+
printer.blockStart();
|
|
419
|
+
|
|
420
|
+
for (let i = 0; i < node.declarations.length; i++) {
|
|
421
|
+
const declaration = node.declarations[i];
|
|
422
|
+
|
|
423
|
+
printer.print(declaration, node);
|
|
424
|
+
|
|
425
|
+
if (i !== node.declarations.length - 1) {
|
|
426
|
+
printer.newline();
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
printer.blockEnd();
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
const Declaration = defineType('Declaration', {
|
|
435
|
+
fields: {
|
|
436
|
+
property: {
|
|
437
|
+
validate: assertValueType('string'),
|
|
438
|
+
},
|
|
439
|
+
value: {
|
|
440
|
+
validate: () =>
|
|
441
|
+
assertOneOf([assertValueType('string'), assertType(CallExpression)]),
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
generate(printer, node) {
|
|
445
|
+
printer.token(node.property);
|
|
446
|
+
printer.token(':');
|
|
447
|
+
printer.space();
|
|
448
|
+
if (typeof node.value === 'string') {
|
|
449
|
+
printer.token(node.value);
|
|
450
|
+
} else {
|
|
451
|
+
printer.print(node.value);
|
|
452
|
+
}
|
|
453
|
+
printer.token(';');
|
|
454
|
+
},
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
//-------------------------------------------------------------------------------
|
|
458
|
+
// At-Rules and directives
|
|
459
|
+
//-------------------------------------------------------------------------------
|
|
460
|
+
const AtRule = defineType('AtRule', {
|
|
461
|
+
fields: {
|
|
462
|
+
name: {
|
|
463
|
+
validate: assertValueType('string'),
|
|
464
|
+
},
|
|
465
|
+
media: {
|
|
466
|
+
validate: assertValueType('string'),
|
|
467
|
+
},
|
|
468
|
+
children: {
|
|
469
|
+
validate: arrayOf(assertOneOf([assertType(Rule)])),
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
generate(printer, node) {
|
|
473
|
+
printer.token(`@${node.name}`);
|
|
474
|
+
printer.space();
|
|
475
|
+
printer.token(node.media);
|
|
476
|
+
printer.space();
|
|
477
|
+
printer.blockStart();
|
|
478
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
479
|
+
printer.print(node.children[i], node);
|
|
480
|
+
if (i !== node.children.length - 1) {
|
|
481
|
+
printer.newline();
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
printer.blockEnd();
|
|
485
|
+
},
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
const AtContent = defineType('AtContent', {
|
|
489
|
+
fields: {},
|
|
490
|
+
generate(printer, node, parent) {
|
|
491
|
+
if (parent.body.indexOf(node) !== 0) {
|
|
492
|
+
printer.maybeNewline();
|
|
493
|
+
}
|
|
494
|
+
printer.token('@content;');
|
|
495
|
+
},
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
const AtReturn = defineType('AtReturn', {
|
|
499
|
+
fields: {
|
|
500
|
+
argument: {
|
|
501
|
+
validate: assertAny,
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
generate(printer, node, parent) {
|
|
505
|
+
if (parent.body.indexOf(node) !== 0) {
|
|
506
|
+
printer.maybeNewline();
|
|
507
|
+
}
|
|
508
|
+
printer.token('@return');
|
|
509
|
+
printer.space();
|
|
510
|
+
printer.print(node.argument, node);
|
|
511
|
+
printer.token(';');
|
|
512
|
+
},
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
//-------------------------------------------------------------------------------
|
|
516
|
+
// Assignment
|
|
517
|
+
//-------------------------------------------------------------------------------
|
|
518
|
+
const Assignment = defineType('Assignment', {
|
|
519
|
+
fields: {
|
|
520
|
+
id: {
|
|
521
|
+
validate: assertType(Identifier),
|
|
522
|
+
},
|
|
523
|
+
init: {
|
|
524
|
+
validate: () =>
|
|
525
|
+
assertOneOf([
|
|
526
|
+
assertType(CallExpression),
|
|
527
|
+
assertType(SassBoolean),
|
|
528
|
+
assertType(SassColor),
|
|
529
|
+
assertType(SassList),
|
|
530
|
+
assertType(SassMap),
|
|
531
|
+
assertType(SassNumber),
|
|
532
|
+
assertType(SassString),
|
|
533
|
+
assertType(SassFunctionCall),
|
|
534
|
+
]),
|
|
535
|
+
},
|
|
536
|
+
default: {
|
|
537
|
+
optional: true,
|
|
538
|
+
validate: assertValueType('boolean'),
|
|
539
|
+
},
|
|
540
|
+
global: {
|
|
541
|
+
optional: true,
|
|
542
|
+
validate: assertValueType('boolean'),
|
|
543
|
+
},
|
|
544
|
+
},
|
|
545
|
+
generate(printer, node, parent) {
|
|
546
|
+
printer.print(node.id, node);
|
|
547
|
+
printer.token(':');
|
|
548
|
+
printer.space();
|
|
549
|
+
printer.print(node.init, node);
|
|
550
|
+
|
|
551
|
+
if (node.default) {
|
|
552
|
+
printer.space();
|
|
553
|
+
printer.token('!default');
|
|
554
|
+
|
|
555
|
+
if (node.global) {
|
|
556
|
+
printer.space();
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (node.global) {
|
|
561
|
+
printer.token('!global');
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
printer.token(';');
|
|
565
|
+
|
|
566
|
+
if (parent) {
|
|
567
|
+
// We have a couple of options for the block we may be operating in, in
|
|
568
|
+
// this case we'll check for children or body and check if the collection
|
|
569
|
+
// exists
|
|
570
|
+
const collection = parent.children || parent.body;
|
|
571
|
+
|
|
572
|
+
// If we have a collection, and there are more than one element in the
|
|
573
|
+
// collection, then we can safely determine if we need to apply a newline
|
|
574
|
+
// after an assignment
|
|
575
|
+
if (collection && collection.length > 1) {
|
|
576
|
+
const assignments = collection.filter(
|
|
577
|
+
(node) => node.type === Assignment.type
|
|
578
|
+
);
|
|
579
|
+
if (
|
|
580
|
+
assignments.length === 1 ||
|
|
581
|
+
assignments.indexOf(node) === assignments.length - 1
|
|
582
|
+
) {
|
|
583
|
+
printer.newline();
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
const AssignmentPattern = defineType('AssignmentPattern', {
|
|
591
|
+
fields: {
|
|
592
|
+
left: {
|
|
593
|
+
validate: assertType(Identifier),
|
|
594
|
+
},
|
|
595
|
+
right: {
|
|
596
|
+
validate: assertAny,
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
generate(printer, node) {
|
|
600
|
+
printer.print(node.left, node);
|
|
601
|
+
printer.token(':');
|
|
602
|
+
printer.space();
|
|
603
|
+
printer.print(node.right, node);
|
|
604
|
+
},
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
const RestPattern = defineType('RestPattern', {
|
|
608
|
+
fields: {
|
|
609
|
+
id: {
|
|
610
|
+
validate: assertType(Identifier),
|
|
611
|
+
},
|
|
612
|
+
},
|
|
613
|
+
generate(printer, node, parent) {
|
|
614
|
+
printer.print(node.id, parent);
|
|
615
|
+
printer.token('...');
|
|
616
|
+
},
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
//-------------------------------------------------------------------------------
|
|
620
|
+
// Imports
|
|
621
|
+
//-------------------------------------------------------------------------------
|
|
622
|
+
const SassImport = defineType('SassImport', {
|
|
623
|
+
fields: {
|
|
624
|
+
path: {
|
|
625
|
+
validate: assertValueType('string'),
|
|
626
|
+
},
|
|
627
|
+
},
|
|
628
|
+
generate(printer, node) {
|
|
629
|
+
printer.token('@import');
|
|
630
|
+
printer.space();
|
|
631
|
+
printer.token(`'${node.path}'`);
|
|
632
|
+
printer.token(';');
|
|
633
|
+
},
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
const SassModule = defineType('SassModule', {
|
|
637
|
+
fields: {
|
|
638
|
+
path: {
|
|
639
|
+
validate: assertValueType('string'),
|
|
640
|
+
},
|
|
641
|
+
},
|
|
642
|
+
generate(printer, node) {
|
|
643
|
+
printer.token('@use');
|
|
644
|
+
printer.space();
|
|
645
|
+
printer.token(`'${node.path}'`);
|
|
646
|
+
printer.token(';');
|
|
647
|
+
},
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
const SassForward = defineType('SassForward', {
|
|
651
|
+
fields: {
|
|
652
|
+
path: {
|
|
653
|
+
validate: assertValueType('string'),
|
|
654
|
+
},
|
|
655
|
+
},
|
|
656
|
+
generate(printer, node) {
|
|
657
|
+
printer.token('@forward');
|
|
658
|
+
printer.space();
|
|
659
|
+
printer.token(`'${node.path}'`);
|
|
660
|
+
printer.token(';');
|
|
661
|
+
},
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
//-------------------------------------------------------------------------------
|
|
665
|
+
// Control structures
|
|
666
|
+
//-------------------------------------------------------------------------------
|
|
667
|
+
const IfStatement = defineType('IfStatement', {
|
|
668
|
+
fields: {
|
|
669
|
+
test: {
|
|
670
|
+
validate: assertAny,
|
|
671
|
+
},
|
|
672
|
+
consequent: {
|
|
673
|
+
optional: true,
|
|
674
|
+
validate: assertType(BlockStatement),
|
|
675
|
+
},
|
|
676
|
+
alternate: {
|
|
677
|
+
optional: true,
|
|
678
|
+
validate: () =>
|
|
679
|
+
assertOneOf([assertType(IfStatement), assertType(BlockStatement)]),
|
|
680
|
+
},
|
|
681
|
+
},
|
|
682
|
+
generate(printer, node, parent) {
|
|
683
|
+
if (parent && parent.type === IfStatement.type) {
|
|
684
|
+
printer.space();
|
|
685
|
+
printer.token('if');
|
|
686
|
+
} else {
|
|
687
|
+
printer.token('@if');
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
printer.space();
|
|
691
|
+
printer.print(node.test, node);
|
|
692
|
+
printer.print(node.consequent, node);
|
|
693
|
+
|
|
694
|
+
if (node.alternate) {
|
|
695
|
+
printer.token('@else');
|
|
696
|
+
printer.print(node.alternate, node);
|
|
697
|
+
}
|
|
698
|
+
},
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
//-------------------------------------------------------------------------------
|
|
702
|
+
// Logical expressions
|
|
703
|
+
//-------------------------------------------------------------------------------
|
|
704
|
+
const LogicalExpression = defineType('LogicalExpression', {
|
|
705
|
+
fields: {
|
|
706
|
+
left: {
|
|
707
|
+
validate: assertAny,
|
|
708
|
+
},
|
|
709
|
+
operator: {
|
|
710
|
+
validate: assertValueType('string'),
|
|
711
|
+
},
|
|
712
|
+
right: {
|
|
713
|
+
validate: assertAny,
|
|
714
|
+
},
|
|
715
|
+
},
|
|
716
|
+
generate(printer, node) {
|
|
717
|
+
printer.print(node.left, node);
|
|
718
|
+
printer.space();
|
|
719
|
+
printer.token(node.operator);
|
|
720
|
+
printer.space();
|
|
721
|
+
printer.print(node.right, node);
|
|
722
|
+
},
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
//-------------------------------------------------------------------------------
|
|
726
|
+
// Call expressions
|
|
727
|
+
//-------------------------------------------------------------------------------
|
|
728
|
+
const CallExpression = defineType('CallExpression', {
|
|
729
|
+
fields: {
|
|
730
|
+
callee: {
|
|
731
|
+
validate: assertType(Identifier),
|
|
732
|
+
},
|
|
733
|
+
arguments: {
|
|
734
|
+
optional: true,
|
|
735
|
+
validate: arrayOf(assertAny),
|
|
736
|
+
},
|
|
737
|
+
},
|
|
738
|
+
generate(printer, node) {
|
|
739
|
+
printer.print(node.callee);
|
|
740
|
+
printer.token('(');
|
|
741
|
+
if (Array.isArray(node.arguments)) {
|
|
742
|
+
for (let i = 0; i < node.arguments.length; i++) {
|
|
743
|
+
printer.print(node.arguments[i], node);
|
|
744
|
+
if (i !== node.arguments.length - 1) {
|
|
745
|
+
printer.token(',');
|
|
746
|
+
printer.space();
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
printer.token(')');
|
|
751
|
+
},
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
//-------------------------------------------------------------------------------
|
|
755
|
+
// StyleSheet
|
|
756
|
+
//-------------------------------------------------------------------------------
|
|
757
|
+
const StyleSheet = defineType('StyleSheet', {
|
|
758
|
+
fields: {
|
|
759
|
+
children: {
|
|
760
|
+
validate: () =>
|
|
761
|
+
arrayOf(
|
|
762
|
+
assertOneOf([
|
|
763
|
+
assertType(Assignment),
|
|
764
|
+
assertType(AtRule),
|
|
765
|
+
assertType(Comment),
|
|
766
|
+
assertType(IfStatement),
|
|
767
|
+
assertType(Rule),
|
|
768
|
+
assertType(SassFunction),
|
|
769
|
+
assertType(SassImport),
|
|
770
|
+
assertType(SassMixin),
|
|
771
|
+
assertType(SassMixinCall),
|
|
772
|
+
assertType(Newline),
|
|
773
|
+
])
|
|
774
|
+
),
|
|
775
|
+
},
|
|
776
|
+
},
|
|
777
|
+
generate(printer, node) {
|
|
778
|
+
// TODO: print leading comments
|
|
779
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
780
|
+
printer.print(node.children[i], node);
|
|
781
|
+
if (i !== node.children.length - 1) {
|
|
782
|
+
printer.newline();
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
},
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
//-------------------------------------------------------------------------------
|
|
789
|
+
// Formatting
|
|
790
|
+
//-------------------------------------------------------------------------------
|
|
791
|
+
const Newline = defineType('Newline', {
|
|
792
|
+
generate(printer) {
|
|
793
|
+
printer.newline();
|
|
794
|
+
},
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
module.exports = {
|
|
798
|
+
Assignment,
|
|
799
|
+
AssignmentPattern,
|
|
800
|
+
AtContent,
|
|
801
|
+
AtReturn,
|
|
802
|
+
AtRule,
|
|
803
|
+
BlockStatement,
|
|
804
|
+
CallExpression,
|
|
805
|
+
Comment,
|
|
806
|
+
Declaration,
|
|
807
|
+
IfStatement,
|
|
808
|
+
Identifier,
|
|
809
|
+
LogicalExpression,
|
|
810
|
+
RestPattern,
|
|
811
|
+
Rule,
|
|
812
|
+
SassBoolean,
|
|
813
|
+
SassColor,
|
|
814
|
+
SassForward,
|
|
815
|
+
SassFunction,
|
|
816
|
+
SassFunctionCall,
|
|
817
|
+
SassImport,
|
|
818
|
+
SassNumber,
|
|
819
|
+
SassString,
|
|
820
|
+
SassList,
|
|
821
|
+
SassMap,
|
|
822
|
+
SassMapProperty,
|
|
823
|
+
SassModule,
|
|
824
|
+
SassValue,
|
|
825
|
+
SassMixin,
|
|
826
|
+
SassMixinCall,
|
|
827
|
+
StyleSheet,
|
|
828
|
+
|
|
829
|
+
// Formatting
|
|
830
|
+
Newline,
|
|
831
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright OctaviaFlow
|
|
3
|
+
* Author: Vishal Kumar
|
|
4
|
+
* Created: 11/November/2025
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const definitions = require('./definitions');
|
|
14
|
+
|
|
15
|
+
const types = Object.keys(definitions).reduce((acc, key) => {
|
|
16
|
+
const { builder, type } = definitions[key];
|
|
17
|
+
return {
|
|
18
|
+
...acc,
|
|
19
|
+
[type]: builder,
|
|
20
|
+
};
|
|
21
|
+
}, {});
|
|
22
|
+
|
|
23
|
+
module.exports = {
|
|
24
|
+
definitions,
|
|
25
|
+
types,
|
|
26
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright OctaviaFlow
|
|
3
|
+
* Author: Vishal Kumar
|
|
4
|
+
* Created: 11/November/2025
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
function defineType(type, { fields = {}, generate } = {}) {
|
|
14
|
+
const keys = Object.keys(fields);
|
|
15
|
+
|
|
16
|
+
if (typeof generate !== 'function') {
|
|
17
|
+
throw new Error(`Expected a \`generate\` method for type \`${type}\``);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Support both object builder pattern and variadic arguments pattern. This
|
|
22
|
+
* allows type builders to be called using `t.Identifer({ name: 'value' })`
|
|
23
|
+
* alongside `t.Identifier('value')`.
|
|
24
|
+
*
|
|
25
|
+
* In some cases, the former syntax is preferred while in other situations
|
|
26
|
+
* (particularly for primitive values) we would rather use the latter pattern.
|
|
27
|
+
*/
|
|
28
|
+
function builder(...args) {
|
|
29
|
+
let input = args;
|
|
30
|
+
|
|
31
|
+
// If we are given a builder object, let's use that instead. The logic for
|
|
32
|
+
// this can get tricky, so we are looking to see if we are given one
|
|
33
|
+
// argument that is an object without a `type` annotation. This should be a
|
|
34
|
+
// decent heuristic to toggle between when someone is using an object
|
|
35
|
+
// builder versus passing in multiple arguments (or one argument in the case
|
|
36
|
+
// of types with one field)
|
|
37
|
+
if (
|
|
38
|
+
args.length === 1 &&
|
|
39
|
+
typeof args[0] === 'object' &&
|
|
40
|
+
!Array.isArray(args[0]) &&
|
|
41
|
+
args[0].type === undefined
|
|
42
|
+
) {
|
|
43
|
+
input = args[0];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const node = {
|
|
47
|
+
type,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
for (let i = 0; i < keys.length; i++) {
|
|
51
|
+
const key = keys[i];
|
|
52
|
+
const field = fields[key];
|
|
53
|
+
const value = Array.isArray(input) ? input[i] : input[key];
|
|
54
|
+
|
|
55
|
+
if (value !== undefined) {
|
|
56
|
+
field.validate(value, node);
|
|
57
|
+
node[key] = value;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!field.optional && value === undefined) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Expected field '${key}' to be defined for type ${type}`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return node;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
builder,
|
|
73
|
+
generate,
|
|
74
|
+
type,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = {
|
|
79
|
+
defineType,
|
|
80
|
+
};
|