@sealcode/sealgen 0.14.4 → 0.14.5

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,36 @@
1
+ import { BuildContext } from "esbuild";
2
+ export declare abstract class Builder {
3
+ abstract ownsFile(file_path: string): boolean;
4
+ abstract getName(): string;
5
+ abstract _build(): Promise<void>;
6
+ abstract dispose(): Promise<void>;
7
+ constructor();
8
+ ongoing_build: Promise<void> | null;
9
+ build(notifier?: (message: string) => void): Promise<void>;
10
+ }
11
+ export declare class CSSBuilder extends Builder {
12
+ getName(): string;
13
+ private context_promise;
14
+ ownsFile(file_path: string): boolean;
15
+ dispose(): Promise<void>;
16
+ getBuildContext(): Promise<BuildContext>;
17
+ _build(): Promise<void>;
18
+ }
19
+ export declare class FontsBuilder extends Builder {
20
+ getName(): string;
21
+ ownsFile(file_path: string): boolean;
22
+ dispose(): Promise<void>;
23
+ _build(): Promise<void>;
24
+ }
25
+ export declare class BackendTSBuilder extends Builder {
26
+ getName(): string;
27
+ ownsFile(file_path: string): boolean;
28
+ dispose(): Promise<void>;
29
+ _build(): Promise<void>;
30
+ }
31
+ export declare class FrontendTSBuilder extends Builder {
32
+ getName(): string;
33
+ ownsFile(file_path: string): boolean;
34
+ dispose(): Promise<void>;
35
+ _build(): Promise<void>;
36
+ }
@@ -0,0 +1 @@
1
+ export declare const embeddable_file_extensions: string[];
@@ -0,0 +1,4 @@
1
+ export declare const load_assets_plugin: {
2
+ name: string;
3
+ setup(build: any): void;
4
+ };
@@ -0,0 +1,4 @@
1
+ export declare const rewrite_asset_imports_plugin: {
2
+ name: string;
3
+ setup(build: any): void;
4
+ };
@@ -0,0 +1 @@
1
+ export declare function generateStimulusControllers(): Promise<void>;
@@ -30,7 +30,7 @@ export declare abstract class StatefulPage<State extends Record<string, unknown>
30
30
  readonly message: "";
31
31
  }>;
32
32
  constructor();
33
- abstract wrapInLayout(ctx: BaseContext, content: Templatable): Templatable;
33
+ abstract wrapInLayout(ctx: BaseContext, content: Templatable, state: State): Templatable;
34
34
  wrapInForm(state: State, content: Templatable): Templatable;
35
35
  extractActionAndLabel<ActionName extends keyof Actions>(action_description: StatefulPageActionDescription<ActionName>): {
36
36
  action: string;
@@ -0,0 +1 @@
1
+ export declare function jddComponentTemplate(controller_name: string): string;
package/dist/build.js CHANGED
@@ -1,20 +1,14 @@
1
- import { build, context } from "esbuild";
2
1
  import { promises as fs } from "fs";
3
2
  import * as chokidar from "chokidar";
4
- import { default as glob } from "tiny-glob";
5
- import { generate_css_includes } from "./find-css-includes.js";
6
- import { sleep } from "./utils/sleep.js";
7
- import { generateCollections } from "./generate-collections.js";
8
- import { generateRoutes } from "./generate-routes.js";
9
- import { generateComponents } from "./generate-components.js";
10
3
  import { make_notifier } from "./notifier.js";
11
4
  import getPort from "get-port";
12
- import { removeNamedImports } from "../remove-named-imports.js";
13
- import md5 from "md5";
14
5
  import _locreq from "locreq";
15
- import path, { basename, extname } from "path";
16
- import { copyFile } from "fs/promises";
17
- import { FONTS_CONFIG_PATH, getFonts } from "./get-fonts.js";
6
+ import {
7
+ BackendTSBuilder,
8
+ CSSBuilder,
9
+ FontsBuilder,
10
+ FrontendTSBuilder
11
+ } from "./builder.js";
18
12
  const target_locreq = _locreq(process.cwd());
19
13
  async function get_notifier_port() {
20
14
  return getPort({ port: 4e3 });
@@ -29,222 +23,44 @@ async function write_notifier_config(watch, port) {
29
23
  JSON.stringify(config)
30
24
  );
31
25
  }
32
- function addSuffix(filename, suffix) {
33
- return filename.replace(/(\.[a-zA-Z0-9]+)?$/, suffix + "$1");
34
- }
35
- async function makeResourceURL(file_path) {
36
- const hash_base = file_path + (await fs.stat(file_path)).mtimeMs;
37
- const hash = md5(hash_base);
38
- const public_url = "dist/" + addSuffix(basename(file_path), "-" + hash.slice(0, 8));
39
- const public_dir = "public";
40
- await copyFile(
41
- file_path,
42
- target_locreq.resolve(path.resolve(public_dir, public_url))
43
- );
44
- return "/" + public_url;
45
- }
46
- async function build_ts() {
47
- await Promise.all([
48
- generateCollections(),
49
- generateRoutes(),
50
- generateComponents()
51
- ]);
52
- const embeddable_file_extensions = ["svg", "png", "jpg", "jpeg", "webp"];
53
- const entryPoints = (await glob(
54
- `./src/back/**/*.{ts,tsx,${embeddable_file_extensions.join(",")}}`
55
- )).filter((path2) => !path2.includes(".#"));
56
- try {
57
- await build({
58
- entryPoints,
59
- sourcemap: true,
60
- bundle: false,
61
- jsxFactory: "TempstreamJSX.createElement",
62
- outdir: "./dist/back",
63
- logLevel: "info",
64
- platform: "node",
65
- target: "es2022",
66
- format: "esm",
67
- loader: Object.fromEntries(
68
- embeddable_file_extensions.map((ext) => ["." + ext, "js"])
69
- ),
70
- plugins: [
71
- {
72
- name: "sealgen-rewrite-asset-imports",
73
- setup(build2) {
74
- build2.onLoad(
75
- { filter: /\.tsx?$/ },
76
- async (args) => {
77
- let contents = await fs.readFile(
78
- args.path,
79
- "utf8"
80
- );
81
- for (const ext of embeddable_file_extensions) {
82
- const regex = new RegExp(
83
- `^import (\\w+|{[^}]+}) from "([^"]+.${ext})";`,
84
- "gm"
85
- );
86
- contents = contents.replaceAll(
87
- regex,
88
- (line) => {
89
- return line.replace(
90
- `.${ext}"`,
91
- '.js"'
92
- );
93
- }
94
- );
95
- }
96
- contents = removeNamedImports(contents);
97
- return {
98
- contents,
99
- loader: extname(args.path) === ".tsx" ? "tsx" : "ts"
100
- };
101
- }
102
- );
103
- }
104
- },
105
- {
106
- name: "sealgen-load-assets",
107
- setup(build2) {
108
- build2.onLoad(
109
- {
110
- filter: new RegExp(
111
- `\\.(${embeddable_file_extensions.join(
112
- "|"
113
- )})$`
114
- )
115
- },
116
- async (args) => {
117
- let contents = `export const url = "${await makeResourceURL(
118
- args.path
119
- )}";
120
-
121
- import fs from "fs";
122
- let buffer_promise = null;
123
-
124
- export const getBuffer = function(){
125
- if(buffer_promise) return buffer_promise;
126
- buffer_promise = fs.promises.readFile("${args.path}");
127
- return buffer_promise;
128
- }
129
-
130
- export const getContent = async function(){
131
- const buffer = await getBuffer();
132
- return buffer.toString("utf-8");
133
- }
134
-
135
- export const getBase64 = async function(){
136
- const buffer = await getBuffer();
137
- return buffer.toString("base64");
138
- }
139
-
140
- export default { getContent, url, getBase64, getBuffer };
141
- `;
142
- return {
143
- contents
144
- };
145
- }
146
- );
147
- }
148
- }
149
- ]
150
- });
151
- } catch (e) {
152
- console.error(e.message);
153
- }
154
- }
155
- async function complex_build(watch) {
156
- const loader = Object.fromEntries(
157
- ["png", "svg", "jpg", "gif", "jpeg", "otf", "ttf"].map(
158
- (ext) => ["." + ext, "file"]
159
- )
160
- );
161
- let css_build = await context({
162
- entryPoints: ["./src/main.css"],
163
- sourcemap: true,
164
- bundle: true,
165
- outdir: "./public/dist",
166
- logLevel: "info",
167
- // plugins: [sassPlugin()],
168
- loader
169
- });
170
- let ongoing_fonts = false;
171
- let ongoing_ts = false;
172
- let ongoing_css = false;
173
- let ongoing_ts_promise = null;
26
+ async function build(watch) {
27
+ const fonts_builder = new FontsBuilder();
28
+ const backend_ts_builder = new BackendTSBuilder();
29
+ const frontend_ts_builder = new FrontendTSBuilder();
30
+ const css_builder = new CSSBuilder();
31
+ const builders = [
32
+ fonts_builder,
33
+ backend_ts_builder,
34
+ css_builder,
35
+ frontend_ts_builder
36
+ ];
37
+ await Promise.all(builders.map((b) => b.build()));
174
38
  if (watch) {
175
39
  const watcher = chokidar.watch("src", { ignoreInitial: true });
176
40
  const port = await get_notifier_port();
177
41
  const notifier = make_notifier(port);
178
42
  await write_notifier_config(watch, port);
179
- watcher.on("all", async (_, path2) => {
180
- if (!css_build)
181
- return;
182
- if (path2.includes(".#"))
183
- return;
184
- if (path2 == FONTS_CONFIG_PATH) {
185
- if (!ongoing_fonts) {
186
- ongoing_fonts = true;
187
- await getFonts();
188
- ongoing_fonts = false;
189
- }
190
- return;
191
- }
192
- if (path2.endsWith(".css") && !path2.endsWith("/includes.css")) {
193
- if (ongoing_css) {
194
- return;
195
- }
196
- ongoing_css = true;
197
- await generate_css_includes();
198
- try {
199
- await css_build?.rebuild?.();
200
- console.log(`Built main.css [on ${path2}]`);
201
- } catch (e) {
202
- console.error(e.message);
203
- await sleep(200);
204
- css_build?.rebuild?.().catch((e2) => console.error(e2?.message));
205
- }
206
- if (ongoing_ts_promise) {
207
- await ongoing_ts_promise;
208
- }
209
- notifier("css");
210
- ongoing_css = false;
211
- }
212
- if ((path2.endsWith(".ts") || path2.endsWith(".tsx")) && !path2.endsWith("src/back/collections/collections.ts") && !path2.endsWith("src/back/routes/routes.ts") && !path2.endsWith("src/back/routes/urls.ts")) {
213
- if (ongoing_ts) {
214
- return;
43
+ watcher.on("all", async (_, file_path) => {
44
+ console.log("Detected a change!", file_path);
45
+ builders.forEach((builder) => {
46
+ const owns_a_file = builder.ownsFile(file_path);
47
+ if (owns_a_file) {
48
+ builder.build(notifier);
215
49
  }
216
- ongoing_ts = true;
217
- ongoing_ts_promise = build_ts();
218
- await ongoing_ts_promise;
219
- notifier("ts");
220
- ongoing_ts_promise = null;
221
- console.log(`Finished TS build [on ${path2}]`);
222
- ongoing_ts = false;
223
- }
50
+ });
224
51
  });
225
52
  } else {
226
53
  await write_notifier_config(watch);
227
- }
228
- await generate_css_includes();
229
- await Promise.all([css_build.rebuild(), build_ts()]);
230
- if (watch) {
231
- css_build?.rebuild?.();
232
- } else {
233
- css_build.dispose();
54
+ builders.forEach((builder) => {
55
+ builder.dispose();
56
+ });
234
57
  }
235
58
  }
236
59
  async function buildProject({
237
60
  watch
238
61
  }) {
239
62
  try {
240
- await build({
241
- entryPoints: ["./src/front/index.ts"],
242
- sourcemap: true,
243
- outfile: "./public/dist/bundle.js",
244
- logLevel: "info",
245
- bundle: true
246
- });
247
- await complex_build(watch);
63
+ await build(watch);
248
64
  } catch (e) {
249
65
  console.log("CAUGHT!");
250
66
  console.error(e.message);
@@ -0,0 +1,145 @@
1
+ import { build, context } from "esbuild";
2
+ import glob from "tiny-glob";
3
+ import { embeddable_file_extensions } from "./embeddable-file-extensions.js";
4
+ import { load_assets_plugin } from "./esbuild-plugins/load-assets.js";
5
+ import { rewrite_asset_imports_plugin } from "./esbuild-plugins/rewrite-asset-imports.js";
6
+ import { generate_css_includes } from "./find-css-includes.js";
7
+ import { generateCollections } from "./generate-collections.js";
8
+ import { generateComponents } from "./generate-components.js";
9
+ import { generateRoutes } from "./generate-routes.js";
10
+ import { generateStimulusControllers } from "./generate-stimulus.js";
11
+ import { FONTS_CONFIG_PATH, getFonts } from "./get-fonts.js";
12
+ class Builder {
13
+ constructor() {
14
+ this.ongoing_build = null;
15
+ }
16
+ build(notifier) {
17
+ if (!this.ongoing_build) {
18
+ const build2 = this._build().catch((err) => {
19
+ console.error(err);
20
+ }).then(() => {
21
+ notifier?.(this.getName());
22
+ this.ongoing_build = null;
23
+ });
24
+ this.ongoing_build = build2;
25
+ }
26
+ return this.ongoing_build;
27
+ }
28
+ }
29
+ class CSSBuilder extends Builder {
30
+ getName() {
31
+ return "css";
32
+ }
33
+ ownsFile(file_path) {
34
+ return file_path.endsWith(".css") && !file_path.endsWith("/includes.css");
35
+ }
36
+ async dispose() {
37
+ return (await this.getBuildContext()).dispose();
38
+ }
39
+ async getBuildContext() {
40
+ if (!this.context_promise) {
41
+ const loader = Object.fromEntries(
42
+ ["png", "svg", "jpg", "gif", "jpeg", "otf", "ttf"].map(
43
+ (ext) => ["." + ext, "file"]
44
+ )
45
+ );
46
+ const context_promise = context({
47
+ entryPoints: ["./src/main.css"],
48
+ sourcemap: true,
49
+ bundle: true,
50
+ outdir: "./public/dist",
51
+ logLevel: "info",
52
+ // plugins: [sassPlugin()],
53
+ loader
54
+ });
55
+ this.context_promise = context_promise;
56
+ return context_promise;
57
+ } else {
58
+ return this.context_promise;
59
+ }
60
+ }
61
+ async _build() {
62
+ const context2 = await this.getBuildContext();
63
+ await generate_css_includes();
64
+ await context2.rebuild();
65
+ }
66
+ }
67
+ class FontsBuilder extends Builder {
68
+ getName() {
69
+ return "fonts";
70
+ }
71
+ ownsFile(file_path) {
72
+ return file_path == FONTS_CONFIG_PATH;
73
+ }
74
+ async dispose() {
75
+ }
76
+ async _build() {
77
+ return getFonts();
78
+ }
79
+ }
80
+ class BackendTSBuilder extends Builder {
81
+ getName() {
82
+ return "backend-ts";
83
+ }
84
+ ownsFile(file_path) {
85
+ return (file_path.endsWith(".ts") || file_path.endsWith(".tsx")) && !file_path.endsWith("src/back/collections/collections.ts") && !file_path.endsWith("src/back/routes/routes.ts") && !file_path.endsWith("src/back/routes/urls.ts") && !file_path.endsWith("stimulus.ts") && !file_path.startsWith("src/front");
86
+ }
87
+ async dispose() {
88
+ }
89
+ async _build() {
90
+ await Promise.all([
91
+ generateCollections(),
92
+ generateRoutes(),
93
+ generateComponents()
94
+ ]);
95
+ const entryPoints = (await glob(
96
+ `./src/back/**/*.{ts,tsx,${embeddable_file_extensions.join(
97
+ ","
98
+ )}}`
99
+ )).filter((path) => !path.includes(".#"));
100
+ await build({
101
+ entryPoints,
102
+ sourcemap: true,
103
+ bundle: false,
104
+ jsxFactory: "TempstreamJSX.createElement",
105
+ outdir: "./dist/back",
106
+ logLevel: "info",
107
+ platform: "node",
108
+ target: "es2022",
109
+ format: "esm",
110
+ loader: Object.fromEntries(
111
+ embeddable_file_extensions.map((ext) => ["." + ext, "js"])
112
+ ),
113
+ plugins: [rewrite_asset_imports_plugin, load_assets_plugin]
114
+ });
115
+ }
116
+ }
117
+ class FrontendTSBuilder extends Builder {
118
+ getName() {
119
+ return "frontend-ts";
120
+ }
121
+ ownsFile(file_path) {
122
+ return file_path.startsWith("src/front") || file_path.endsWith(".stimulus.ts");
123
+ }
124
+ async dispose() {
125
+ }
126
+ async _build() {
127
+ await generateStimulusControllers();
128
+ await build({
129
+ entryPoints: ["./src/front/index.ts"],
130
+ sourcemap: true,
131
+ outfile: "./public/dist/bundle.js",
132
+ logLevel: "info",
133
+ bundle: true,
134
+ minify: true
135
+ });
136
+ }
137
+ }
138
+ export {
139
+ BackendTSBuilder,
140
+ Builder,
141
+ CSSBuilder,
142
+ FontsBuilder,
143
+ FrontendTSBuilder
144
+ };
145
+ //# sourceMappingURL=builder.js.map
package/dist/cli.js CHANGED
@@ -10,6 +10,7 @@ import { generateRoutes } from "./generate-routes.js";
10
10
  import { makeEnv } from "./make-env.js";
11
11
  import yargs from "yargs/yargs";
12
12
  import { getFonts } from "./get-fonts.js";
13
+ import { generateStimulusControllers } from "./generate-stimulus.js";
13
14
  const actions = {
14
15
  "add-collection": addCollection,
15
16
  "add-route": addRoute,
@@ -26,7 +27,8 @@ const actions = {
26
27
  },
27
28
  "make-env": makeEnv,
28
29
  "add-component": addJDDComponent,
29
- "get-fonts": getFonts
30
+ "get-fonts": getFonts,
31
+ "generate-stimulus": generateStimulusControllers
30
32
  };
31
33
  void async function() {
32
34
  const action = process.argv.at(2);
@@ -0,0 +1,5 @@
1
+ const embeddable_file_extensions = ["svg", "png", "jpg", "jpeg", "webp"];
2
+ export {
3
+ embeddable_file_extensions
4
+ };
5
+ //# sourceMappingURL=embeddable-file-extensions.js.map
@@ -0,0 +1,67 @@
1
+ import { copyFile } from "fs/promises";
2
+ import { promises as fs } from "fs";
3
+ import md5 from "md5";
4
+ import path, { basename } from "node:path";
5
+ import _locreq from "locreq";
6
+ import { embeddable_file_extensions } from "../embeddable-file-extensions.js";
7
+ const target_locreq = _locreq(process.cwd());
8
+ function addSuffix(filename, suffix) {
9
+ return filename.replace(/(\.[a-zA-Z0-9]+)?$/, suffix + "$1");
10
+ }
11
+ async function makeResourceURL(file_path) {
12
+ const hash_base = file_path + (await fs.stat(file_path)).mtimeMs;
13
+ const hash = md5(hash_base);
14
+ const public_url = "dist/" + addSuffix(basename(file_path), "-" + hash.slice(0, 8));
15
+ const public_dir = "public";
16
+ await copyFile(
17
+ file_path,
18
+ target_locreq.resolve(path.resolve(public_dir, public_url))
19
+ );
20
+ return "/" + public_url;
21
+ }
22
+ const load_assets_plugin = {
23
+ name: "sealgen-load-assets",
24
+ setup(build) {
25
+ build.onLoad(
26
+ {
27
+ filter: new RegExp(
28
+ `\\.(${embeddable_file_extensions.join("|")})$`
29
+ )
30
+ },
31
+ async (args) => {
32
+ let contents = `export const url = "${await makeResourceURL(
33
+ args.path
34
+ )}";
35
+
36
+ import fs from "fs";
37
+ let buffer_promise = null;
38
+
39
+ export const getBuffer = function(){
40
+ if(buffer_promise) return buffer_promise;
41
+ buffer_promise = fs.promises.readFile("${args.path}");
42
+ return buffer_promise;
43
+ }
44
+
45
+ export const getContent = async function(){
46
+ const buffer = await getBuffer();
47
+ return buffer.toString("utf-8");
48
+ }
49
+
50
+ export const getBase64 = async function(){
51
+ const buffer = await getBuffer();
52
+ return buffer.toString("base64");
53
+ }
54
+
55
+ export default { getContent, url, getBase64, getBuffer };
56
+ `;
57
+ return {
58
+ contents
59
+ };
60
+ }
61
+ );
62
+ }
63
+ };
64
+ export {
65
+ load_assets_plugin
66
+ };
67
+ //# sourceMappingURL=load-assets.js.map
@@ -0,0 +1,30 @@
1
+ import { promises as fs } from "fs";
2
+ import { extname } from "node:path";
3
+ import { embeddable_file_extensions } from "../embeddable-file-extensions.js";
4
+ import { removeNamedImports } from "../../remove-named-imports.js";
5
+ const rewrite_asset_imports_plugin = {
6
+ name: "sealgen-rewrite-asset-imports",
7
+ setup(build) {
8
+ build.onLoad({ filter: /\.tsx?$/ }, async (args) => {
9
+ let contents = await fs.readFile(args.path, "utf8");
10
+ for (const ext of embeddable_file_extensions) {
11
+ const regex_assets = new RegExp(
12
+ `^import (\\w+|{[^}]+}) from "([^"]+.${ext})";`,
13
+ "gm"
14
+ );
15
+ contents = contents.replaceAll(regex_assets, (line) => {
16
+ return line.replace(`.${ext}"`, '.js"');
17
+ });
18
+ }
19
+ contents = removeNamedImports(contents);
20
+ return {
21
+ contents,
22
+ loader: extname(args.path) === ".tsx" ? "tsx" : "ts"
23
+ };
24
+ });
25
+ }
26
+ };
27
+ export {
28
+ rewrite_asset_imports_plugin
29
+ };
30
+ //# sourceMappingURL=rewrite-asset-imports.js.map
@@ -0,0 +1,41 @@
1
+ import _locreq from "locreq";
2
+ import * as prettier from "prettier";
3
+ import { promises as fs } from "fs";
4
+ import { getPrettierConfig } from "./utils/prettier.js";
5
+ import recursive from "recursive-readdir";
6
+ import path, { dirname } from "path";
7
+ import { toKebabCase, toPascalCase } from "js-convert-case";
8
+ const target_locreq = _locreq(process.cwd());
9
+ async function generateStimulusControllers() {
10
+ const output_file = "src/front/controllers.ts";
11
+ const stimulus_search_dir = target_locreq.resolve("src/");
12
+ const controller_names = (await recursive(stimulus_search_dir)).filter((c) => c.endsWith(".stimulus.ts")).sort();
13
+ const controller_data = controller_names.map(
14
+ (p) => "./" + path.relative(target_locreq.resolve(dirname(output_file)), p).replace(/\.[tj]sx?$/, ".js")
15
+ ).map((relative_path) => {
16
+ const name = relative_path.match(/\/([^\/]+).stimulus/)?.[1];
17
+ return { name, relative_path };
18
+ });
19
+ const content = `// DO NOT EDIT! This file is generated automaticaly with 'npm run generate-stimulus'
20
+
21
+ import * as Turbo from "@hotwired/turbo";
22
+ import { Application } from "stimulus";
23
+ const application = Application.start();
24
+ ${controller_data.map(
25
+ (c) => `
26
+ import { default as ${toPascalCase(c.name)} } from "${c.relative_path}";
27
+ application.register("${toKebabCase(c.name)}", ${toPascalCase(c.name)});`
28
+ ).join("\n")}
29
+
30
+ export { Turbo };
31
+ `;
32
+ await fs.writeFile(
33
+ target_locreq.resolve(output_file),
34
+ prettier.format(content, await getPrettierConfig())
35
+ );
36
+ console.log(`Successfuly generated new ${output_file} file`);
37
+ }
38
+ export {
39
+ generateStimulusControllers
40
+ };
41
+ //# sourceMappingURL=generate-stimulus.js.map
@@ -16,7 +16,8 @@ class StatefulPage extends Mountable {
16
16
  this.wrapInForm(
17
17
  state,
18
18
  await original_render(ctx, state, inputs)
19
- )
19
+ ),
20
+ state
20
21
  );
21
22
  };
22
23
  }
@@ -123,7 +124,7 @@ class StatefulPage extends Mountable {
123
124
  options
124
125
  );
125
126
  } else if (target.indexOf(item) === -1) {
126
- destination.push(item);
127
+ destination[index] = item;
127
128
  }
128
129
  });
129
130
  return destination;