@atlaspack/build-cache 2.13.3-canary.56 → 2.13.3-canary.561

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.
@@ -0,0 +1,67 @@
1
+ /**
2
+ * A Map implementation that can exceed Node 24's Map size limit.
3
+ *
4
+ * LargeMap works around the Maximum maps size limit by using multiple internal Maps,
5
+ * creating a new one when the current Map reaches the size limit.
6
+ *
7
+ * This is a minimal implementation supporting only has/get/set - intended as a
8
+ * temporary solution until we no longer need large JS serialization
9
+ */
10
+ export class LargeMap<K, V> {
11
+ maps: Map<K, V>[];
12
+ maxSize: number;
13
+ singleMap: boolean;
14
+ lastMap: Map<K, V>;
15
+
16
+ constructor(maxSize: number = Math.pow(2, 23)) {
17
+ this.lastMap = new Map();
18
+ this.maps = [this.lastMap];
19
+ this.singleMap = true;
20
+ this.maxSize = maxSize;
21
+ }
22
+
23
+ set(key: K, value: V): this {
24
+ // Update existing key if found
25
+ if (!this.singleMap) {
26
+ for (let map of this.maps) {
27
+ if (map.has(key)) {
28
+ map.set(key, value);
29
+ return this;
30
+ }
31
+ }
32
+ }
33
+
34
+ // Otherwise, add to last map
35
+ if (this.lastMap.size >= this.maxSize) {
36
+ this.lastMap = new Map();
37
+ this.maps.push(this.lastMap);
38
+ this.singleMap = false;
39
+ }
40
+
41
+ this.lastMap.set(key, value);
42
+
43
+ return this;
44
+ }
45
+
46
+ get(key: K): V | undefined {
47
+ if (this.singleMap) {
48
+ return this.lastMap.get(key);
49
+ }
50
+
51
+ for (let map of this.maps) {
52
+ if (map.has(key)) {
53
+ return map.get(key);
54
+ }
55
+ }
56
+ return undefined;
57
+ }
58
+
59
+ has(key: K): boolean {
60
+ for (let map of this.maps) {
61
+ if (map.has(key)) {
62
+ return true;
63
+ }
64
+ }
65
+ return false;
66
+ }
67
+ }
@@ -1,5 +1,3 @@
1
- // @flow
2
-
3
1
  const buildCaches: Array<Map<any, any>> = [];
4
2
 
5
3
  export function createBuildCache<K, V>(): Map<K, V> {
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './buildCache';
2
+ export * from './serializer';
3
+ export * from './serializerCore';
@@ -1,9 +1,15 @@
1
- // @flow
1
+ import {getFeatureFlag} from '@atlaspack/feature-flags';
2
+
2
3
  import {createBuildCache} from './buildCache';
4
+ import {LargeMap} from './LargeMap';
3
5
  import {serializeRaw, deserializeRaw} from './serializerCore';
4
6
 
5
7
  export {serializeRaw, deserializeRaw} from './serializerCore';
6
8
 
9
+ // flow-to-ts helpers
10
+ export type Class<T> = new (...args: any[]) => T;
11
+ // /flow-to-ts helpers
12
+
7
13
  const nameToCtor: Map<string, Class<any>> = new Map();
8
14
  const ctorToName: Map<Class<any>, string> = new Map();
9
15
 
@@ -49,7 +55,7 @@ function shallowCopy(object: any) {
49
55
  return object;
50
56
  }
51
57
 
52
- function isBuffer(object) {
58
+ function isBuffer(object: any) {
53
59
  return (
54
60
  object.buffer instanceof ArrayBuffer ||
55
61
  (typeof SharedArrayBuffer !== 'undefined' &&
@@ -57,13 +63,25 @@ function isBuffer(object) {
57
63
  );
58
64
  }
59
65
 
60
- function shouldContinueMapping(value) {
66
+ function shouldContinueMapping(value: any) {
61
67
  return value && typeof value === 'object' && value.$$raw !== true;
62
68
  }
63
69
 
64
- function mapObject(object: any, fn: (val: any) => any, preOrder = false): any {
65
- let cache = new Map();
66
- let memo = new Map();
70
+ interface MapLike<K, V> {
71
+ get(key: K): V | undefined;
72
+ set(key: K, value: V): unknown;
73
+ has(key: K): boolean;
74
+ }
75
+
76
+ function mapObject(object: any, fn: (val?: any) => any, preOrder = false): any {
77
+ // Use LargeMap to work around Node 24's Map size limit
78
+ // when the feature flag is enabled
79
+ let cache: MapLike<any, any> = getFeatureFlag('useLargeMapInBuildCache')
80
+ ? new LargeMap<any, any>()
81
+ : new Map<any, any>();
82
+ let memo: MapLike<any, any> = getFeatureFlag('useLargeMapInBuildCache')
83
+ ? new LargeMap<any, any>()
84
+ : new Map<any, any>();
67
85
 
68
86
  // Memoize the passed function to ensure it always returns the exact same
69
87
  // output by reference for the same input. This is important to maintain
@@ -211,8 +229,8 @@ export function restoreDeserializedObject(object: any): any {
211
229
  );
212
230
  }
213
231
 
214
- if (typeof ctor.deserialize === 'function') {
215
- return ctor.deserialize(value.value);
232
+ if (typeof (ctor as any).deserialize === 'function') {
233
+ return (ctor as any).deserialize(value.value);
216
234
  }
217
235
 
218
236
  value = value.value;
@@ -223,7 +241,7 @@ export function restoreDeserializedObject(object: any): any {
223
241
  });
224
242
  }
225
243
 
226
- const serializeCache = createBuildCache();
244
+ const serializeCache = createBuildCache<any, Buffer>();
227
245
 
228
246
  export function serialize(object: any): Buffer {
229
247
  let cached = serializeCache.get(object);
@@ -0,0 +1,4 @@
1
+ import v8 from 'v8';
2
+
3
+ export let serializeRaw: (arg1?: any) => Buffer = v8.serialize;
4
+ export let deserializeRaw: (arg1: Buffer) => any = v8.deserialize;
@@ -0,0 +1,151 @@
1
+ import assert from 'assert';
2
+ import {LargeMap} from '../src/LargeMap';
3
+
4
+ describe('LargeMap', () => {
5
+ describe('basic operations', () => {
6
+ it('should set and get values', () => {
7
+ const map = new LargeMap<string, number>();
8
+ map.set('a', 1);
9
+ map.set('b', 2);
10
+
11
+ assert.equal(map.get('a'), 1);
12
+ assert.equal(map.get('b'), 2);
13
+ });
14
+
15
+ it('should return undefined for missing keys', () => {
16
+ const map = new LargeMap<string, number>();
17
+ assert.equal(map.get('missing'), undefined);
18
+ });
19
+
20
+ it('should check if key exists with has()', () => {
21
+ const map = new LargeMap<string, number>();
22
+ map.set('a', 1);
23
+
24
+ assert.equal(map.has('a'), true);
25
+ assert.equal(map.has('b'), false);
26
+ });
27
+
28
+ it('should update existing key in single map', () => {
29
+ const map = new LargeMap<string, number>();
30
+ map.set('a', 1);
31
+ map.set('a', 2);
32
+
33
+ assert.equal(map.get('a'), 2);
34
+ });
35
+
36
+ it('should return this from set() for chaining', () => {
37
+ const map = new LargeMap<string, number>();
38
+ const result = map.set('a', 1).set('b', 2).set('c', 3);
39
+
40
+ assert.equal(result, map);
41
+ assert.equal(map.get('a'), 1);
42
+ assert.equal(map.get('b'), 2);
43
+ assert.equal(map.get('c'), 3);
44
+ });
45
+ });
46
+
47
+ describe('multiple internal maps', () => {
48
+ it('should create new internal map when maxSize is reached', () => {
49
+ const map = new LargeMap<number, number>(3); // Small maxSize for testing
50
+
51
+ map.set(1, 1);
52
+ map.set(2, 2);
53
+ map.set(3, 3);
54
+ assert.equal(map.maps.length, 1);
55
+
56
+ map.set(4, 4); // Should trigger new internal map
57
+ assert.equal(map.maps.length, 2);
58
+
59
+ map.set(5, 5);
60
+ map.set(6, 6);
61
+ assert.equal(map.maps.length, 2);
62
+
63
+ map.set(7, 7); // Should trigger another new internal map
64
+ assert.equal(map.maps.length, 3);
65
+ });
66
+
67
+ it('should find values across multiple internal maps', () => {
68
+ const map = new LargeMap<number, string>(2);
69
+
70
+ map.set(1, 'one');
71
+ map.set(2, 'two');
72
+ map.set(3, 'three');
73
+ map.set(4, 'four');
74
+ map.set(5, 'five');
75
+
76
+ assert.equal(map.get(1), 'one');
77
+ assert.equal(map.get(2), 'two');
78
+ assert.equal(map.get(3), 'three');
79
+ assert.equal(map.get(4), 'four');
80
+ assert.equal(map.get(5), 'five');
81
+ });
82
+
83
+ it('should check has() across multiple internal maps', () => {
84
+ const map = new LargeMap<number, string>(2);
85
+
86
+ map.set(1, 'one');
87
+ map.set(2, 'two');
88
+ map.set(3, 'three');
89
+
90
+ assert.equal(map.has(1), true);
91
+ assert.equal(map.has(2), true);
92
+ assert.equal(map.has(3), true);
93
+ assert.equal(map.has(4), false);
94
+ });
95
+
96
+ it('should update existing key in earlier internal map', () => {
97
+ const map = new LargeMap<number, string>(2);
98
+
99
+ map.set(1, 'one');
100
+ map.set(2, 'two');
101
+ map.set(3, 'three'); // Goes to second map
102
+
103
+ // Update key in first map
104
+ map.set(1, 'ONE');
105
+
106
+ assert.equal(map.get(1), 'ONE');
107
+ assert.equal(map.maps.length, 2); // Should not create new map
108
+ });
109
+ });
110
+
111
+ describe('type handling', () => {
112
+ it('should work with object keys', () => {
113
+ const map = new LargeMap<object, string>();
114
+ const key1 = {id: 1};
115
+ const key2 = {id: 2};
116
+
117
+ map.set(key1, 'first');
118
+ map.set(key2, 'second');
119
+
120
+ assert.equal(map.get(key1), 'first');
121
+ assert.equal(map.get(key2), 'second');
122
+ assert.equal(map.get({id: 1}), undefined); // Different reference
123
+ });
124
+
125
+ it('should work with null and undefined values', () => {
126
+ const map = new LargeMap<string, any>();
127
+
128
+ map.set('null', null);
129
+ map.set('undefined', undefined);
130
+
131
+ assert.equal(map.get('null'), null);
132
+ assert.equal(map.get('undefined'), undefined);
133
+ assert.equal(map.has('null'), true);
134
+ assert.equal(map.has('undefined'), true);
135
+ });
136
+
137
+ it('should work with various key types', () => {
138
+ const map = new LargeMap<any, string>();
139
+
140
+ map.set(1, 'number');
141
+ map.set('str', 'string');
142
+ map.set(true, 'boolean');
143
+ map.set(null, 'null');
144
+
145
+ assert.equal(map.get(1), 'number');
146
+ assert.equal(map.get('str'), 'string');
147
+ assert.equal(map.get(true), 'boolean');
148
+ assert.equal(map.get(null), 'null');
149
+ });
150
+ });
151
+ });
@@ -1,5 +1,3 @@
1
- // @flow
2
-
3
1
  import assert from 'assert';
4
2
  import sinon from 'sinon';
5
3
 
@@ -151,8 +149,8 @@ describe('serializer', () => {
151
149
 
152
150
  it('should serialize a cyclic class', () => {
153
151
  class Foo {
154
- x: ?Foo;
155
- constructor(x: ?Foo) {
152
+ x: Foo | null | undefined;
153
+ constructor(x?: Foo | null) {
156
154
  this.x = x;
157
155
  }
158
156
  }
@@ -193,8 +191,8 @@ describe('serializer', () => {
193
191
 
194
192
  it('should serialize a cyclic class and copy on write', () => {
195
193
  class Foo {
196
- x: ?Foo;
197
- constructor(x: ?Foo) {
194
+ x: Foo | null | undefined;
195
+ constructor(x?: Foo | null) {
198
196
  this.x = x;
199
197
  }
200
198
  }
@@ -257,7 +255,7 @@ describe('serializer', () => {
257
255
  class Outer {
258
256
  inner: Inner;
259
257
 
260
- constructor(inner) {
258
+ constructor(inner: any) {
261
259
  this.inner = inner;
262
260
  }
263
261
 
@@ -272,7 +270,7 @@ describe('serializer', () => {
272
270
  class Inner {
273
271
  x: number;
274
272
 
275
- constructor(x) {
273
+ constructor(x: any) {
276
274
  this.x = x;
277
275
  }
278
276
 
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "include": ["src"],
4
+ "compilerOptions": {
5
+ "composite": true
6
+ },
7
+ "references": [
8
+ {
9
+ "path": "../feature-flags/tsconfig.json"
10
+ }
11
+ ]
12
+ }