@navita/core 0.0.0 → 0.0.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.
@@ -1,306 +1,53 @@
1
- import { createRequire as createRequire$1 } from 'module';
2
- const require = createRequire$1(import.meta.url);
3
- import { createRenderer as createRenderer$1 } from 'fela';
4
- import { renderToString as renderToString$1 } from 'fela-tools';
5
- import { isProxy } from 'node:util/types';
6
- import * as v8 from 'node:v8';
7
-
8
- const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
9
- const charLength = chars.length;
10
- function nextString(id, className = '') {
11
- if (id <= charLength) {
12
- return chars[id - 1] + className;
13
- }
14
- return nextString(id / charLength | 0, chars[(id - 1) % charLength] + className);
15
- }
16
- function generateString(getId, blacklist = []) {
17
- const className = nextString(getId());
18
- if (blacklist.includes(className.toLowerCase())) {
19
- return generateString(getId, blacklist);
20
- }
21
- return className;
22
- }
23
- function className(blacklist = []) {
24
- return (renderer)=>{
25
- renderer.generateClassName = ()=>{
26
- return generateString(renderer.getNextRuleIdentifier, blacklist);
27
- };
28
- renderer.generateAnimationName = ()=>{
29
- return generateString(renderer.getNextKeyframeIdentifier, blacklist);
30
- };
31
- return renderer;
32
- };
33
- }
34
-
35
- function createResult(renderer, filePath, isServer) {
36
- const values = renderer.evaluatedResults.get(filePath) || [];
37
- if (isServer && renderer.classPropertyList) {
38
- // Todo: this is just a POC. It needs to be cleaned up, but it's a different build for
39
- // the server. On the client we'll use browser APIs.
40
- // We're using this class list to be able to merge during runtime.
41
- const newResult = (values || []).map((item)=>{
42
- if (typeof item === "string") {
43
- const rules = item.split(' ').map((x)=>renderer.classPropertyList.has(x) ? [
44
- x,
45
- [
46
- ...renderer.classPropertyList.get(x)
47
- ]
48
- ] : false).filter(Boolean);
49
- return `new (require('@navita/css').ClassList)("${item}", ${JSON.stringify(rules)})`;
1
+ import { Engine } from '@navita/engine';
2
+ import { transformer } from '@navita/swc';
3
+ import { evaluateAndProcess } from './evaluateAndProcess.mjs';
4
+ import 'node:path';
5
+ import 'node:vm';
6
+ import 'node:fs';
7
+ import 'enhanced-resolve';
8
+ import '@navita/adapter';
9
+
10
+ function startsWithRSCDirective(content) {
11
+ return /^(['"])use client\1;?/.test(content);
12
+ }
13
+
14
+ function createRenderer({ resolver , readFile , importMap =[] , engineOptions }) {
15
+ const engine = new Engine(engineOptions);
16
+ return {
17
+ engine,
18
+ async transformAndProcess ({ content , filePath }) {
19
+ const [newContent, { result , dependencies }] = await Promise.all([
20
+ transformer(content, {
21
+ filename: filePath,
22
+ importMap
23
+ }),
24
+ evaluateAndProcess({
25
+ type: 'entryPoint',
26
+ source: content,
27
+ filePath,
28
+ resolver,
29
+ readFile,
30
+ importMap,
31
+ engine
32
+ })
33
+ ]);
34
+ const finalContent = [];
35
+ // Check if content starts with "use client".
36
+ if (startsWithRSCDirective(content)) {
37
+ const [directive, ...restContent] = newContent.trim().split('\n');
38
+ finalContent.push(directive, result, ...restContent);
39
+ } else {
40
+ finalContent.push(result, newContent.trim());
50
41
  }
51
- return JSON.stringify(item);
52
- });
53
- return `const $$evaluatedValues = [${newResult}];`;
54
- }
55
- return `const $$evaluatedValues = ${JSON.stringify(values || [])};`;
56
- }
57
- function evaluatedResult() {
58
- return (renderer)=>{
59
- renderer.evaluatedResults = new Map();
60
- renderer.setEvaluatedResult = (fileName, index, value)=>{
61
- if (!renderer.evaluatedResults.has(fileName)) {
62
- renderer.evaluatedResults.set(fileName, []);
63
- }
64
- renderer.evaluatedResults.get(fileName)[index] = value;
65
- };
66
- renderer.getResult = (filePath, isServer)=>{
67
- const result = createResult(renderer, filePath, isServer || false);
68
42
  return {
69
- result,
70
- get css () {
71
- return renderer.renderToString?.(filePath) || "";
72
- }
43
+ result: finalContent.join('\n'),
44
+ dependencies,
45
+ usedIds: engine.getUsedCacheIds([
46
+ filePath
47
+ ])
73
48
  };
74
- };
75
- return renderer;
76
- };
77
- }
78
-
79
- function isMediaQuery(rule) {
80
- return rule.startsWith('@media');
81
- }
82
- function isSupportsQuery(rule) {
83
- return rule.startsWith('@supports');
84
- }
85
- function isPseudoSelector(rule) {
86
- return rule.startsWith(':');
87
- }
88
- function wrapWith(atRule, query, content) {
89
- return query.length > 0 ? `${atRule} ${query} {${content}}` : content;
90
- }
91
- function createCssRule(selector, properties) {
92
- return `${selector}{${properties.map((rule)=>`${cssifyProperty(rule.property)}: ${rule.value};`).join('')}}`;
93
- }
94
- function generateCombinedMediaQuery(currentMediaQuery, nestedMediaQuery) {
95
- if (currentMediaQuery.length === 0) {
96
- return nestedMediaQuery;
97
- }
98
- return `${currentMediaQuery} and ${nestedMediaQuery}`;
99
- }
100
- function cssifyProperty(property) {
101
- return property.replace(/([a-zA-Z])([A-Z])/g, '$1-$2').toLowerCase();
102
- }
103
- function isObject(value) {
104
- return typeof value === 'object' && !Array.isArray(value);
105
- }
106
- function collectAndGroupRules(cssObj, currentSupportsQuery = '', currentMediaQuery = '', currentPseudoSelector = '', groupedRules = {}) {
107
- for (const [property, value] of Object.entries(cssObj)){
108
- if (!isObject(value)) {
109
- if (!groupedRules[currentSupportsQuery]) {
110
- groupedRules[currentSupportsQuery] = {};
111
- }
112
- const supportsGroup = groupedRules[currentSupportsQuery];
113
- if (!supportsGroup[currentMediaQuery]) {
114
- supportsGroup[currentMediaQuery] = {};
115
- }
116
- const mediaGroup = supportsGroup[currentMediaQuery];
117
- if (!mediaGroup[currentPseudoSelector]) {
118
- mediaGroup[currentPseudoSelector] = [];
119
- }
120
- mediaGroup[currentPseudoSelector].push({
121
- property,
122
- value
123
- });
124
- continue;
125
- }
126
- if (isMediaQuery(property)) {
127
- collectAndGroupRules(value, currentSupportsQuery, generateCombinedMediaQuery(currentMediaQuery, property.slice(6).trim()), currentPseudoSelector, groupedRules);
128
- continue;
129
- }
130
- if (isSupportsQuery(property)) {
131
- collectAndGroupRules(value, generateCombinedMediaQuery(currentSupportsQuery, property.slice(9).trim()), currentMediaQuery, currentPseudoSelector, groupedRules);
132
- continue;
133
49
  }
134
- if (isPseudoSelector(property)) {
135
- collectAndGroupRules(value, currentSupportsQuery, currentMediaQuery, currentPseudoSelector + property, groupedRules);
136
- }
137
- }
138
- return groupedRules;
139
- }
140
- function serializeCss(selector, cssObj) {
141
- const processPseudoSelectors = (pseudoSelectors)=>Object.entries(pseudoSelectors).map(([pseudo, properties])=>createCssRule(`${selector}${pseudo}`, properties)).join('');
142
- const processMediaQueries = (mediaQueries)=>Object.entries(mediaQueries).map(([media, pseudoSelectors])=>wrapWith('@media', media, processPseudoSelectors(pseudoSelectors))).join('');
143
- const rules = collectAndGroupRules(cssObj);
144
- return Object.entries(rules).map(([supports, mediaQueries])=>wrapWith('@supports', supports, processMediaQueries(mediaQueries))).join('');
145
- }
146
- function nestedStatic() {
147
- // Fela doesn't handle nested static styles correctly. This is a workaround for now.
148
- // https://github.com/robinweser/fela/issues/876
149
- // https://github.com/robinweser/fela/issues/465#issuecomment-594396150
150
- // https://github.com/robinweser/fela/issues/863
151
- return (renderer)=>{
152
- const { renderStatic } = renderer;
153
- renderer.renderStatic = (staticStyle, selector)=>{
154
- return renderStatic(serializeCss(selector, staticStyle), undefined);
155
- };
156
- return renderer;
157
- };
158
- }
159
-
160
- function renderToString() {
161
- return (renderer)=>{
162
- renderer.renderToString = (fileName)=>{
163
- if (!renderer.getScopedCache || !renderer.accessedKeys) {
164
- return renderToString$1(renderer);
165
- }
166
- if (fileName) {
167
- return renderToString$1({
168
- ...renderer,
169
- cache: renderer.getScopedCache(fileName)
170
- });
171
- }
172
- const newCache = {};
173
- for (const keys of renderer.accessedKeys.values()){
174
- for (const key of keys){
175
- const item = renderer.cache[key];
176
- if (!item) {
177
- continue;
178
- }
179
- newCache[key] = item;
180
- }
181
- }
182
- return renderToString$1({
183
- ...renderer,
184
- cache: newCache
185
- });
186
- };
187
- return renderer;
188
50
  };
189
51
  }
190
52
 
191
- function scopedCache() {
192
- return (renderer)=>{
193
- let currentFileName;
194
- const ensureCache = ()=>{
195
- if (isProxy(renderer.cache)) {
196
- return;
197
- }
198
- renderer.cache = new Proxy(renderer.cache, {
199
- getOwnPropertyDescriptor (target, key) {
200
- const result = Reflect.getOwnPropertyDescriptor(target, key);
201
- const { accessedKeys } = renderer;
202
- if (!currentFileName) {
203
- return result;
204
- }
205
- if (!accessedKeys.has(currentFileName)) {
206
- accessedKeys.set(currentFileName, new Set());
207
- }
208
- accessedKeys.get(currentFileName)?.add(key);
209
- return result;
210
- }
211
- });
212
- };
213
- renderer.setFilePath = (filePath)=>{
214
- currentFileName = filePath;
215
- // After we've set the file path, we need to ensure that the cache is a proxy,
216
- // and set up to record accessed keys.
217
- // Without a filePath, the proxy on the cache doesn't make sense, so this is a good place to do it.
218
- ensureCache();
219
- };
220
- renderer.accessedKeys = new Map();
221
- renderer.clearAccessedKeys = (filePath)=>{
222
- renderer.accessedKeys.get(filePath)?.clear();
223
- };
224
- renderer.getScopedCache = (filePath)=>{
225
- const { accessedKeys } = renderer;
226
- const newCache = {};
227
- if (accessedKeys.has(filePath)) {
228
- const keys = accessedKeys.get(filePath);
229
- for (const key of keys){
230
- const item = renderer.cache[key];
231
- if (!item) {
232
- continue;
233
- }
234
- newCache[key] = item;
235
- }
236
- }
237
- return newCache;
238
- };
239
- renderer.classPropertyList = new Map();
240
- renderer.subscribe((message)=>{
241
- if (message.type === 'RULE') {
242
- const { declaration , className } = message;
243
- const [property] = declaration.split(':');
244
- if (!property) {
245
- return;
246
- }
247
- const { classPropertyList } = renderer;
248
- if (!classPropertyList.has(className)) {
249
- classPropertyList.set(className, new Set());
250
- }
251
- classPropertyList.get(className)?.add(property);
252
- }
253
- });
254
- return renderer;
255
- };
256
- }
257
-
258
- function serializedCache(keys = []) {
259
- return (renderer)=>{
260
- renderer.serialize = ()=>{
261
- const obj = {};
262
- for (const key of keys){
263
- const value = renderer[key];
264
- if (isProxy(value)) {
265
- obj[key] = Object.assign({}, value);
266
- } else {
267
- obj[key] = value;
268
- }
269
- }
270
- return v8.serialize(obj).toString('base64');
271
- };
272
- renderer.deserialize = (serialized)=>{
273
- if (!serialized) {
274
- return;
275
- }
276
- const data = v8.deserialize(Buffer.from(serialized, 'base64'));
277
- for (const [key, value] of Object.entries(data)){
278
- renderer[key] = value;
279
- }
280
- };
281
- return renderer;
282
- };
283
- }
284
-
285
- function createRenderer() {
286
- return createRenderer$1({
287
- enhancers: [
288
- className([
289
- 'ad'
290
- ]),
291
- scopedCache(),
292
- serializedCache([
293
- 'cache',
294
- 'classPropertyList',
295
- 'accessedKeys',
296
- 'uniqueRuleIdentifier',
297
- 'uniqueKeyframeIdentifier'
298
- ]),
299
- evaluatedResult(),
300
- renderToString(),
301
- nestedStatic()
302
- ]
303
- });
304
- }
305
-
306
53
  export { createRenderer };
@@ -1,14 +1,36 @@
1
- import { Renderer } from './createRenderer.js';
2
- import 'fela';
3
- import '@navita/types';
1
+ import { Engine } from '@navita/engine';
2
+ import { ImportMap } from '@navita/types';
4
3
 
5
- interface Params {
4
+ type FilePath = string;
5
+ type ResolverCache = Record<FilePath, unknown>;
6
+ type NodeModuleCache = Record<FilePath, unknown>;
7
+
8
+ type FilePathWithType = string;
9
+ type ModuleCache = Map<FilePathWithType, {
6
10
  source: string;
7
- key?: string;
8
- filepath: string;
9
- isServer?: boolean;
10
- renderer: Renderer;
11
+ compiledFn: () => Promise<{
12
+ dependencies: string[];
13
+ exports: Record<string, unknown>;
14
+ }>;
15
+ }>;
16
+ interface Caches {
17
+ nodeModuleCache?: NodeModuleCache;
18
+ resolverCache?: ResolverCache;
19
+ moduleCache?: ModuleCache;
20
+ }
21
+ type Types = 'entryPoint' | 'dependency';
22
+ interface Output<Type extends Types> {
23
+ result: Type extends 'entryPoint' ? string : Record<string, unknown>;
24
+ dependencies: string[];
11
25
  }
12
- declare function evaluateAndProcess({ source, renderer, filepath }: Params): Promise<void>;
26
+ declare function evaluateAndProcess<Type extends 'entryPoint' | 'dependency'>({ type, filePath, source, engine, resolver, readFile, importMap, nodeModuleCache, resolverCache, moduleCache, }: {
27
+ source: string;
28
+ filePath: string;
29
+ type: Type;
30
+ engine: Engine;
31
+ resolver: (filePath: string, request: string) => Promise<string>;
32
+ readFile: (filePath: string) => Promise<string>;
33
+ importMap: ImportMap;
34
+ } & Caches): Promise<Output<Type>>;
13
35
 
14
- export { evaluateAndProcess };
36
+ export { Caches, evaluateAndProcess };