@reliverse/dler 1.7.22 → 1.7.23

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 (31) hide show
  1. package/bin/app/cmds.d.ts +1 -1
  2. package/bin/app/cmds.js +1 -1
  3. package/bin/app/inject/cmd.d.ts +0 -24
  4. package/bin/app/inject/cmd.js +0 -47
  5. package/bin/app/{spell → magic}/cmd.d.ts +2 -0
  6. package/bin/app/magic/cmd.js +101 -0
  7. package/bin/app/pub/impl.js +4 -2
  8. package/bin/libs/cfg/cfg-mod.d.ts +1 -1
  9. package/bin/libs/cfg/cfg-mod.js +1 -1
  10. package/bin/libs/cfg/rse/rse-impl/rse-create.js +1 -1
  11. package/bin/libs/cfg/rse/rse-impl/rse-define.d.ts +10 -10
  12. package/bin/libs/cfg/rse/rse-mod.d.ts +1 -1
  13. package/bin/libs/cfg/rse/rse-mod.js +1 -1
  14. package/bin/libs/sdk/sdk-impl/cmds/inject/inject-impl-mod.d.ts +63 -0
  15. package/bin/libs/sdk/sdk-impl/cmds/inject/inject-impl-mod.js +237 -0
  16. package/bin/libs/sdk/sdk-impl/config/info.js +1 -1
  17. package/bin/libs/sdk/sdk-impl/magic/apply.d.ts +63 -0
  18. package/bin/libs/sdk/sdk-impl/{spell/applyMagicSpells.js → magic/apply.js} +223 -70
  19. package/bin/libs/sdk/sdk-impl/{spell → magic}/spells.d.ts +16 -1
  20. package/bin/libs/sdk/sdk-impl/{spell → magic}/spells.js +41 -1
  21. package/bin/libs/sdk/sdk-mod.d.ts +4 -4
  22. package/bin/libs/sdk/sdk-mod.js +2 -2
  23. package/package.json +2 -2
  24. package/bin/app/inject/impl.d.ts +0 -5
  25. package/bin/app/inject/impl.js +0 -159
  26. package/bin/app/spell/cmd.js +0 -47
  27. package/bin/libs/sdk/sdk-impl/spell/applyMagicSpells.d.ts +0 -38
  28. /package/bin/app/{spell → magic}/old.d.ts +0 -0
  29. /package/bin/app/{spell → magic}/old.js +0 -0
  30. /package/bin/libs/cfg/rse/rse-impl/{rse-inject.d.ts → rse-comments.d.ts} +0 -0
  31. /package/bin/libs/cfg/rse/rse-impl/{rse-inject.js → rse-comments.js} +0 -0
package/bin/app/cmds.d.ts CHANGED
@@ -12,5 +12,5 @@ export declare const getMigrateCmd: () => Promise<Command>;
12
12
  export declare const getPubCmd: () => Promise<Command>;
13
13
  export declare const getRemptsCmd: () => Promise<Command>;
14
14
  export declare const getRenameCmd: () => Promise<Command>;
15
- export declare const getSpellCmd: () => Promise<Command>;
15
+ export declare const getMagicCmd: () => Promise<Command>;
16
16
  export declare const getSplitCmd: () => Promise<Command>;
package/bin/app/cmds.js CHANGED
@@ -12,5 +12,5 @@ export const getMigrateCmd = async () => loadCommand("migrate");
12
12
  export const getPubCmd = async () => loadCommand("pub");
13
13
  export const getRemptsCmd = async () => loadCommand("rempts");
14
14
  export const getRenameCmd = async () => loadCommand("rename");
15
- export const getSpellCmd = async () => loadCommand("spell");
15
+ export const getMagicCmd = async () => loadCommand("magic");
16
16
  export const getSplitCmd = async () => loadCommand("split");
@@ -1,24 +0,0 @@
1
- declare const _default: import("@reliverse/rempts").Command<{
2
- dev: {
3
- type: "boolean";
4
- description: string;
5
- };
6
- cwd: {
7
- type: "string";
8
- description: string;
9
- };
10
- files: {
11
- type: "positional";
12
- description: string;
13
- default: string;
14
- };
15
- comment: {
16
- type: "string";
17
- description: string;
18
- };
19
- tscPaths: {
20
- type: "string";
21
- description: string;
22
- };
23
- }>;
24
- export default _default;
@@ -1,47 +0,0 @@
1
- import { relinka } from "@reliverse/relinka";
2
- import { defineArgs, defineCommand } from "@reliverse/rempts";
3
- import { useTsExpectError } from "./impl.js";
4
- export default defineCommand({
5
- meta: {
6
- name: "expect",
7
- version: "1.0.0",
8
- description: "Inject `@ts-expect-error` above lines where TS errors occur"
9
- },
10
- args: defineArgs({
11
- dev: {
12
- type: "boolean",
13
- description: "Run the CLI in dev mode"
14
- },
15
- cwd: {
16
- type: "string",
17
- description: "The working directory to run the CLI in"
18
- },
19
- files: {
20
- type: "positional",
21
- description: `'auto' or path(s) to line references file(s)`,
22
- default: "auto"
23
- },
24
- comment: {
25
- type: "string",
26
- description: "Override the comment line to insert. Default is `// @ts-expect-error TODO: fix ts`"
27
- },
28
- tscPaths: {
29
- type: "string",
30
- description: "Optional: specify path(s) to restrict TSC processing (only effective when using 'auto')"
31
- }
32
- }),
33
- async run({ args }) {
34
- if (args.dev) {
35
- relinka("verbose", "Using dev mode");
36
- }
37
- let pathsTsc = args.tscPaths;
38
- if (pathsTsc === void 0 && args.files === "auto") {
39
- pathsTsc = "./tsconfig.json";
40
- }
41
- await useTsExpectError({
42
- files: [args.files],
43
- comment: args.comment,
44
- tscPaths: [pathsTsc]
45
- });
46
- }
47
- });
@@ -11,10 +11,12 @@ declare const _default: import("@reliverse/rempts").Command<{
11
11
  concurrency: {
12
12
  type: "number";
13
13
  description: string;
14
+ default: number;
14
15
  };
15
16
  batchSize: {
16
17
  type: "number";
17
18
  description: string;
19
+ default: number;
18
20
  };
19
21
  stopOnError: {
20
22
  type: "boolean";
@@ -0,0 +1,101 @@
1
+ import { defineArgs, defineCommand } from "@reliverse/rempts";
2
+ import { applyMagicSpells } from "../../libs/sdk/sdk-impl/magic/apply.js";
3
+ import { formatError } from "../../libs/sdk/sdk-impl/utils/utils-error-cwd.js";
4
+ export default defineCommand({
5
+ meta: {
6
+ name: "magic",
7
+ version: "1.0.0",
8
+ description: `Apply magic directives to files.
9
+ Target Types:
10
+ 1. Distribution Targets (dist-*):
11
+ - dist-npm: Process files in dist-npm/bin
12
+ - dist-jsr: Process files in dist-jsr/bin
13
+ - dist-libs: Process all libraries in dist-libs
14
+ - dist-libs/<lib>: Process specific library (e.g., dist-libs/sdk)
15
+ For dist-* targets, magic directives are first searched in src/ directory,
16
+ then applied to corresponding files in the distribution directories.
17
+ 2. Custom Targets:
18
+ - Any directory name that is not dist-* (e.g., "my-output", "custom-build")
19
+ For custom targets, magic directives are processed directly in the target files.
20
+ No source directory scanning is performed.
21
+ Examples:
22
+ # Process all distribution targets
23
+ dler magic dist-npm dist-jsr dist-libs
24
+ # Process specific library
25
+ dler magic dist-libs/sdk
26
+ # Process custom target
27
+ dler magic my-custom-output
28
+ # Mix distribution and custom targets
29
+ dler magic dist-npm my-custom-output`
30
+ },
31
+ args: defineArgs({
32
+ targets: {
33
+ type: "array",
34
+ description: `Targets to process. Can be:
35
+ - Distribution targets: dist-npm, dist-jsr, dist-libs, dist-libs/<lib>
36
+ - Custom targets: any directory name that is not dist-*`,
37
+ required: true
38
+ },
39
+ lib: {
40
+ type: "string",
41
+ description: "Library name to process (e.g., sdk, cfg). Only valid with dist-libs target."
42
+ },
43
+ concurrency: {
44
+ type: "number",
45
+ description: "Number of files to process in parallel (default: 4)",
46
+ default: 4
47
+ },
48
+ batchSize: {
49
+ type: "number",
50
+ description: "Number of files to process in each batch (default: 100)",
51
+ default: 100
52
+ },
53
+ stopOnError: {
54
+ type: "boolean",
55
+ description: "Stop processing on first error (default: true)",
56
+ default: true
57
+ }
58
+ }),
59
+ async run({ args }) {
60
+ const { targets, lib, concurrency, batchSize, stopOnError } = args;
61
+ if (lib) {
62
+ if (!targets?.includes("dist-libs")) {
63
+ throw new Error(
64
+ "The 'lib' parameter can only be used with 'dist-libs' target. Example: dler magic dist-libs/sdk"
65
+ );
66
+ }
67
+ if (targets.some((t) => t.startsWith("dist-libs/") && t !== `dist-libs/${lib}`)) {
68
+ throw new Error(
69
+ "Cannot specify both 'lib' parameter and dist-libs/<lib> in targets. Use one or the other."
70
+ );
71
+ }
72
+ }
73
+ try {
74
+ const finalTargets = targets?.map(
75
+ (target) => target === "dist-libs" && lib ? `${target}/${lib}` : target
76
+ ) ?? [];
77
+ const distTargets = finalTargets.filter((t) => t.startsWith("dist-"));
78
+ const customTargets = finalTargets.filter((t) => !t.startsWith("dist-"));
79
+ if (distTargets.length > 0) {
80
+ console.log("\nProcessing distribution targets:");
81
+ for (const target of distTargets) {
82
+ console.log(` - ${target} (will scan src/ for magic directives)`);
83
+ }
84
+ }
85
+ if (customTargets.length > 0) {
86
+ console.log("\nProcessing custom targets:");
87
+ for (const target of customTargets) {
88
+ console.log(` - ${target} (will process magic directives directly in target files)`);
89
+ }
90
+ }
91
+ await applyMagicSpells(finalTargets, {
92
+ concurrency,
93
+ batchSize,
94
+ stopOnError
95
+ });
96
+ console.log("\n\u2728 Magic spells applied successfully!");
97
+ } catch (error) {
98
+ throw new Error(`\u274C Processing failed: ${formatError(error)}`);
99
+ }
100
+ }
101
+ });
@@ -4,8 +4,8 @@ import fs from "@reliverse/relifso";
4
4
  import { relinka } from "@reliverse/relinka";
5
5
  import { getConfigDler } from "../../libs/sdk/sdk-impl/config/load.js";
6
6
  import { processLibraryFlow } from "../../libs/sdk/sdk-impl/library-flow.js";
7
+ import { applyMagicSpells } from "../../libs/sdk/sdk-impl/magic/apply.js";
7
8
  import { processRegularFlow } from "../../libs/sdk/sdk-impl/regular-flow.js";
8
- import { applyMagicSpells } from "../../libs/sdk/sdk-impl/spell/applyMagicSpells.js";
9
9
  import { finalizeBuildPub } from "../../libs/sdk/sdk-impl/utils/finalize.js";
10
10
  import { resolveAllCrossLibs } from "../../libs/sdk/sdk-impl/utils/resolve-cross-libs.js";
11
11
  import { removeDistFolders } from "../../libs/sdk/sdk-impl/utils/utils-clean.js";
@@ -45,7 +45,9 @@ export async function dlerPub(isDev, config) {
45
45
  await processRegularFlow(timer, isDev, effectiveConfig);
46
46
  await processLibraryFlow(timer, isDev, effectiveConfig);
47
47
  await resolveAllCrossLibs();
48
- await applyMagicSpells(["dist-jsr", "dist-npm", "dist-libs"]);
48
+ if (isDev) {
49
+ await applyMagicSpells(["dist-jsr", "dist-npm", "dist-libs"]);
50
+ }
49
51
  relinka("log", "[processDistDirectory] dist-npm");
50
52
  await processDistDirectory("dist-npm", "~");
51
53
  relinka("log", "[processDistDirectory] dist-jsr");
@@ -10,7 +10,7 @@ export { DEFAULT_CONFIG_RSE, PROJECT_FRAMEWORK_FILES } from "./rse/rse-impl/rse-
10
10
  export { defineConfigRse } from "./rse/rse-impl/rse-define.js";
11
11
  export { detectProjectFramework, getPackageJson, getPackageJsonSafe, detectProject, detectProjectsWithRseConfig, detectFeatures, } from "./rse/rse-impl/rse-detect.js";
12
12
  export { generateConfigFiles, generateProjectConfigs } from "./rse/rse-impl/rse-gen-cfg.js";
13
- export { injectSectionComments } from "./rse/rse-impl/rse-inject.js";
13
+ export { injectSectionComments } from "./rse/rse-impl/rse-comments.js";
14
14
  export { migrateRseConfig } from "./rse/rse-impl/rse-migrate.js";
15
15
  export { getRseConfigPath } from "./rse/rse-impl/rse-path.js";
16
16
  export { askRseConfigType } from "./rse/rse-impl/rse-prompts.js";
@@ -49,7 +49,7 @@ export {
49
49
  detectFeatures
50
50
  } from "./rse/rse-impl/rse-detect.js";
51
51
  export { generateConfigFiles, generateProjectConfigs } from "./rse/rse-impl/rse-gen-cfg.js";
52
- export { injectSectionComments } from "./rse/rse-impl/rse-inject.js";
52
+ export { injectSectionComments } from "./rse/rse-impl/rse-comments.js";
53
53
  export { migrateRseConfig } from "./rse/rse-impl/rse-migrate.js";
54
54
  export { getRseConfigPath } from "./rse/rse-impl/rse-path.js";
55
55
  export { askRseConfigType } from "./rse/rse-impl/rse-prompts.js";
@@ -5,12 +5,12 @@ import { confirmPrompt } from "@reliverse/rempts";
5
5
  import { Value } from "@sinclair/typebox/value";
6
6
  import { execaCommand } from "execa";
7
7
  import { addDevDependency } from "nypm";
8
+ import { injectSectionComments } from "./rse-comments.js";
8
9
  import { cliDomainDocs } from "./rse-consts.js";
9
10
  import { UNKNOWN_VALUE, rseName, DEFAULT_DOMAIN, RSE_SCHEMA_DEV } from "./rse-consts.js";
10
11
  import { generateDefaultRulesForProject, getDefaultRseConfig } from "./rse-def-utils.js";
11
12
  import { DEFAULT_CONFIG_RSE } from "./rse-default.js";
12
13
  import { getPackageJson, detectFeatures } from "./rse-detect.js";
13
- import { injectSectionComments } from "./rse-inject.js";
14
14
  import { getRseConfigPath } from "./rse-path.js";
15
15
  import { readRseConfig } from "./rse-read.js";
16
16
  import { rseSchema } from "./rse-schema.js";
@@ -10,11 +10,11 @@ export declare const defineConfigRse: (userConfig?: Partial<RseConfig>) => {
10
10
  projectDomain?: string | undefined;
11
11
  projectGitService?: "none" | "github" | "gitlab" | "bitbucket" | undefined;
12
12
  projectDeployService?: "none" | "vercel" | "netlify" | "railway" | "deno" | undefined;
13
- projectPackageManager?: "pnpm" | "bun" | "npm" | "yarn" | undefined;
13
+ projectPackageManager?: "npm" | "bun" | "pnpm" | "yarn" | undefined;
14
14
  projectState?: "creating" | "created" | undefined;
15
- projectCategory?: "cli" | "unknown" | "website" | "vscode" | "browser" | "library" | "mobile" | undefined;
15
+ projectCategory?: "browser" | "cli" | "unknown" | "website" | "vscode" | "library" | "mobile" | undefined;
16
16
  projectSubcategory?: "unknown" | "e-commerce" | "tool" | undefined;
17
- projectFramework?: "unknown" | "vscode" | "nextjs" | "vite" | "svelte" | "remix" | "astro" | "nuxt" | "solid" | "qwik" | "vue" | "wxt" | "lynx" | "react-native" | "expo" | "capacitor" | "ionic" | "electron" | "tauri" | "neutralino" | "rempts" | "citty" | "commander" | "cac" | "meow" | "yargs" | "webextension" | "browser-extension" | "npm-jsr" | undefined;
17
+ projectFramework?: "npm-jsr" | "unknown" | "vscode" | "nextjs" | "vite" | "svelte" | "remix" | "astro" | "nuxt" | "solid" | "qwik" | "vue" | "wxt" | "lynx" | "react-native" | "expo" | "capacitor" | "ionic" | "electron" | "tauri" | "neutralino" | "rempts" | "citty" | "commander" | "cac" | "meow" | "yargs" | "webextension" | "browser-extension" | undefined;
18
18
  projectTemplate?: "unknown" | "blefnk/relivator-nextjs-template" | "blefnk/relivator-docker-template" | "blefnk/next-react-ts-src-minimal" | "blefnk/all-in-one-nextjs-template" | "blefnk/create-t3-app" | "blefnk/create-next-app" | "blefnk/astro-starlight-template" | "blefnk/versator-nextjs-template" | "blefnk/relivator-lynxjs-template" | "blefnk/relivator-react-native-template" | "reliverse/template-browser-extension" | "microsoft/vscode-extension-samples" | "microsoft/vscode-extension-template" | "rsetarter-template" | "blefnk/deno-cli-tutorial" | undefined;
19
19
  projectTemplateDate?: string | undefined;
20
20
  features?: {
@@ -38,15 +38,15 @@ export declare const defineConfigRse: (userConfig?: Partial<RseConfig>) => {
38
38
  analytics?: "unknown" | "vercel" | undefined;
39
39
  authentication?: "unknown" | "better-auth" | "clerk" | "next-auth" | "supabase-auth" | "auth0" | undefined;
40
40
  api?: "unknown" | "hono" | "trpc" | "graphql" | "rest" | undefined;
41
- testing?: "unknown" | "bun" | "vitest" | "jest" | "playwright" | "cypress" | undefined;
41
+ testing?: "bun" | "unknown" | "vitest" | "jest" | "playwright" | "cypress" | undefined;
42
42
  stateManagement?: "unknown" | "zustand" | "jotai" | "redux-toolkit" | undefined;
43
43
  formManagement?: "unknown" | "react-hook-form" | "formik" | undefined;
44
44
  styling?: "unknown" | "tailwind" | "styled-components" | "css-modules" | "sass" | undefined;
45
45
  uiComponents?: "unknown" | "shadcn-ui" | "chakra-ui" | "material-ui" | undefined;
46
46
  databaseLibrary?: "unknown" | "drizzle" | "prisma" | "supabase" | undefined;
47
47
  databaseProvider?: "unknown" | "pg" | "mysql" | "sqlite" | "mongodb" | undefined;
48
- linting?: "unknown" | "eslint" | undefined;
49
- formatting?: "unknown" | "biome" | undefined;
48
+ linting?: "eslint" | "unknown" | undefined;
49
+ formatting?: "biome" | "unknown" | undefined;
50
50
  payment?: "unknown" | "stripe" | undefined;
51
51
  monitoring?: "unknown" | "sentry" | undefined;
52
52
  logging?: "unknown" | "axiom" | undefined;
@@ -75,7 +75,7 @@ export declare const defineConfigRse: (userConfig?: Partial<RseConfig>) => {
75
75
  indentStyle?: "space" | "tab" | undefined;
76
76
  quoteMark?: "single" | "double" | undefined;
77
77
  semicolons?: boolean | undefined;
78
- trailingComma?: "all" | "none" | "es5" | undefined;
78
+ trailingComma?: "none" | "es5" | "all" | undefined;
79
79
  bracketSpacing?: boolean | undefined;
80
80
  arrowParens?: "always" | "avoid" | undefined;
81
81
  tabWidth?: number | undefined;
@@ -83,7 +83,7 @@ export declare const defineConfigRse: (userConfig?: Partial<RseConfig>) => {
83
83
  dontRemoveComments?: boolean | undefined;
84
84
  shouldAddComments?: boolean | undefined;
85
85
  typeOrInterface?: "type" | "interface" | "mixed" | undefined;
86
- importOrRequire?: "import" | "mixed" | "require" | undefined;
86
+ importOrRequire?: "mixed" | "import" | "require" | undefined;
87
87
  cjsToEsm?: boolean | undefined;
88
88
  modernize?: {
89
89
  replaceFs?: boolean | undefined;
@@ -96,7 +96,7 @@ export declare const defineConfigRse: (userConfig?: Partial<RseConfig>) => {
96
96
  importSymbol?: string | undefined;
97
97
  } | undefined;
98
98
  monorepo?: {
99
- type?: "none" | "turborepo" | "nx" | "pnpm" | "bun" | undefined;
99
+ type?: "none" | "bun" | "turborepo" | "nx" | "pnpm" | undefined;
100
100
  packages?: string[] | undefined;
101
101
  sharedPackages?: string[] | undefined;
102
102
  } | undefined;
@@ -113,7 +113,7 @@ export declare const defineConfigRse: (userConfig?: Partial<RseConfig>) => {
113
113
  repoBranch?: string | undefined;
114
114
  repoPrivacy?: "unknown" | "public" | "private" | undefined;
115
115
  projectArchitecture?: "unknown" | "fullstack" | "separated" | undefined;
116
- projectRuntime?: "bun" | "deno" | "node" | undefined;
116
+ projectRuntime?: "node" | "bun" | "deno" | undefined;
117
117
  skipPromptsUseAutoBehavior?: boolean | undefined;
118
118
  deployBehavior?: "prompt" | "autoYes" | "autoNo" | undefined;
119
119
  depsBehavior?: "prompt" | "autoYes" | "autoNo" | undefined;
@@ -8,7 +8,7 @@ export * from "./rse-impl/rse-default";
8
8
  export * from "./rse-impl/rse-define";
9
9
  export * from "./rse-impl/rse-detect";
10
10
  export * from "./rse-impl/rse-gen-cfg";
11
- export * from "./rse-impl/rse-inject";
11
+ export * from "./rse-impl/rse-comments";
12
12
  export * from "./rse-impl/rse-migrate";
13
13
  export * from "./rse-impl/rse-path";
14
14
  export * from "./rse-impl/rse-prompts";
@@ -8,7 +8,7 @@ export * from "./rse-impl/rse-default.js";
8
8
  export * from "./rse-impl/rse-define.js";
9
9
  export * from "./rse-impl/rse-detect.js";
10
10
  export * from "./rse-impl/rse-gen-cfg.js";
11
- export * from "./rse-impl/rse-inject.js";
11
+ export * from "./rse-impl/rse-comments.js";
12
12
  export * from "./rse-impl/rse-migrate.js";
13
13
  export * from "./rse-impl/rse-path.js";
14
14
  export * from "./rse-impl/rse-prompts.js";
@@ -0,0 +1,63 @@
1
+ /**
2
+ * inject-impl-mod.ts
3
+ * -----------
4
+ * Utility for injecting content into files at specific line/column positions.
5
+ *
6
+ * Fully async -– works with Node, Bun, and other
7
+ * runtimes that support the `fs/promises` API.
8
+ *
9
+ * @author blefnk
10
+ */
11
+ export interface InjectionConfig {
12
+ /** Absolute or relative path to target file. */
13
+ filePath: string;
14
+ /** 1-based line number (1 == first visible line in editors) */
15
+ line: number;
16
+ /** 1-based column number (1 == first visible column in editors) */
17
+ column?: number;
18
+ /** String or array of strings to inject. */
19
+ content: string | string[];
20
+ /** If `true`, inserts string (or first element of array) content on a new line AFTER the target line. */
21
+ createNewLine?: boolean;
22
+ /** Configuration for comment wrapping. */
23
+ commentsMode?: {
24
+ /** Whether to wrap content in comments. */
25
+ activate: boolean;
26
+ /** Whether to use JSDoc style for multiline comments. */
27
+ useJsdocForMultiline?: boolean;
28
+ };
29
+ }
30
+ export interface InjectionResult {
31
+ /** Absolute or relative path to target file. */
32
+ filePath: string;
33
+ /** Whether the injection was successful. */
34
+ success: boolean;
35
+ /** Error message if injection failed. */
36
+ error?: string;
37
+ /** Generated source map if available. */
38
+ sourcemap?: string;
39
+ }
40
+ /**
41
+ * Injects content into the given file at the requested location.
42
+ *
43
+ * @param filePath Absolute or relative path to target file.
44
+ * @param line 1-based line number (1 == first visible line in editors, internally == 0).
45
+ * @param column 1-based column number (optional, 1 == first visible column in editors, internally == 0).
46
+ * @param content String or array of strings to inject.
47
+ * @param createNewLine If `true`, inserts string (or first element of array) content on a new line AFTER the target line.
48
+ * @param commentsMode If `true`, wraps injected content in file-type comments.
49
+ * @param generateSourceMap If `true`, generates a source map for the transformation.
50
+ *
51
+ * @returns `true` on success, `false` on failure (errors are logged).
52
+ */
53
+ export declare function injectAtLocation(filePath: string, line: number, column: number | undefined, content: string | string[], createNewLine?: boolean, commentsMode?: {
54
+ activate: boolean;
55
+ }, generateSourceMap?: boolean): Promise<boolean>;
56
+ /**
57
+ * Processes multiple injections efficiently by grouping by file and applying in reverse order.
58
+ *
59
+ * @param configs Array of {@link InjectionConfig}.
60
+ * @param generateSourceMaps If `true`, generates source maps for transformations.
61
+ * @returns Array of {@link InjectionResult} in the same order as `configs`.
62
+ */
63
+ export declare function injectMultiple(configs: InjectionConfig[], generateSourceMaps?: boolean): Promise<InjectionResult[]>;
@@ -0,0 +1,237 @@
1
+ import MagicString from "magic-string";
2
+ import { promises as fs } from "node:fs";
3
+ import * as path from "node:path";
4
+ import { isBinaryExt } from "../../utils/binary.js";
5
+ export async function injectAtLocation(filePath, line, column, content, createNewLine = false, commentsMode = { activate: false }, generateSourceMap = false) {
6
+ try {
7
+ await validateInput(filePath, line, column);
8
+ const originalContent = await fs.readFile(filePath, "utf8");
9
+ const magicString = new MagicString(originalContent, {
10
+ filename: path.basename(filePath)
11
+ });
12
+ const pieces = Array.isArray(content) ? content : [content];
13
+ const preparedContent = prepareContentForInjection(
14
+ pieces,
15
+ commentsMode,
16
+ path.extname(filePath),
17
+ createNewLine
18
+ );
19
+ const success = injectWithMagicString(
20
+ magicString,
21
+ originalContent,
22
+ line,
23
+ column,
24
+ preparedContent,
25
+ createNewLine
26
+ );
27
+ if (!success) return false;
28
+ const result = magicString.toString();
29
+ await fs.writeFile(filePath, result, "utf8");
30
+ if (generateSourceMap) {
31
+ const map = magicString.generateMap({
32
+ source: filePath,
33
+ file: `${filePath}.map`,
34
+ includeContent: true,
35
+ hires: true
36
+ });
37
+ await fs.writeFile(`${filePath}.map`, map.toString(), "utf8");
38
+ }
39
+ return true;
40
+ } catch (err) {
41
+ console.error(`[injector] ${filePath}: ${err.message}`);
42
+ return false;
43
+ }
44
+ }
45
+ export async function injectMultiple(configs, generateSourceMaps = false) {
46
+ const configsByFile = /* @__PURE__ */ new Map();
47
+ configs.forEach((config, index) => {
48
+ if (!configsByFile.has(config.filePath)) {
49
+ configsByFile.set(config.filePath, []);
50
+ }
51
+ configsByFile.get(config.filePath)?.push({ config, originalIndex: index });
52
+ });
53
+ const results = new Array(configs.length);
54
+ for (const [filePath, fileConfigs] of configsByFile) {
55
+ try {
56
+ const firstConfig = fileConfigs[0]?.config;
57
+ if (!firstConfig) {
58
+ throw new Error(`No valid configs found for file: ${filePath}`);
59
+ }
60
+ await validateInput(firstConfig.filePath, firstConfig.line, firstConfig.column);
61
+ const originalContent = await fs.readFile(filePath, "utf8");
62
+ const magicString = new MagicString(originalContent, {
63
+ filename: path.basename(filePath)
64
+ });
65
+ const sortedConfigs = [...fileConfigs].sort((a, b) => {
66
+ const aLine = a.config.line;
67
+ const bLine = b.config.line;
68
+ if (aLine !== bLine) return bLine - aLine;
69
+ const aCol = a.config.column ?? Number.MAX_SAFE_INTEGER;
70
+ const bCol = b.config.column ?? Number.MAX_SAFE_INTEGER;
71
+ return bCol - aCol;
72
+ });
73
+ let allSuccessful = true;
74
+ for (const { config, originalIndex } of sortedConfigs) {
75
+ const { line, column, content, createNewLine, commentsMode } = config;
76
+ const pieces = Array.isArray(content) ? content : [content];
77
+ const preparedContent = prepareContentForInjection(
78
+ pieces,
79
+ commentsMode,
80
+ path.extname(filePath),
81
+ createNewLine ?? false
82
+ );
83
+ const success = injectWithMagicString(
84
+ magicString,
85
+ originalContent,
86
+ line,
87
+ column,
88
+ preparedContent,
89
+ createNewLine ?? false
90
+ );
91
+ results[originalIndex] = success ? { filePath, success } : {
92
+ filePath,
93
+ success: false,
94
+ error: `Injection failed for ${filePath} at line ${line}`
95
+ };
96
+ if (!success) {
97
+ allSuccessful = false;
98
+ }
99
+ }
100
+ if (allSuccessful) {
101
+ const result = magicString.toString();
102
+ await fs.writeFile(filePath, result, "utf8");
103
+ if (generateSourceMaps) {
104
+ const map = magicString.generateMap({
105
+ source: filePath,
106
+ file: `${filePath}.map`,
107
+ includeContent: true,
108
+ hires: true
109
+ });
110
+ await fs.writeFile(`${filePath}.map`, map.toString(), "utf8");
111
+ }
112
+ }
113
+ } catch (err) {
114
+ for (const { originalIndex } of fileConfigs) {
115
+ results[originalIndex] = {
116
+ filePath,
117
+ success: false,
118
+ error: `File processing failed: ${err.message}`
119
+ };
120
+ }
121
+ }
122
+ }
123
+ return results;
124
+ }
125
+ async function validateInput(filePath, line, column) {
126
+ await fs.access(filePath).catch(() => {
127
+ throw new Error("File does not exist.");
128
+ });
129
+ if (await isBinaryExt(filePath)) {
130
+ throw new Error("Cannot inject into binary files.");
131
+ }
132
+ if (!Number.isInteger(line) || line < 1)
133
+ throw new Error("`line` must be a positive 1-based integer.");
134
+ if (column !== void 0 && (!Number.isInteger(column) || column < 1))
135
+ throw new Error("`column` must be a positive 1-based integer when provided.");
136
+ }
137
+ function getCharacterIndex(content, line1Based, column1Based) {
138
+ const lines = content.split(/\r?\n/);
139
+ const eolLength = content.includes("\r\n") ? 2 : 1;
140
+ while (lines.length < line1Based) {
141
+ lines.push("");
142
+ }
143
+ let index = 0;
144
+ for (let i = 0; i < line1Based - 1; i++) {
145
+ index += lines[i]?.length ?? 0;
146
+ if (i < lines.length - 1) index += eolLength;
147
+ }
148
+ const targetLine = lines[line1Based - 1] || "";
149
+ const lineStart = index;
150
+ const lineEnd = lineStart + targetLine.length;
151
+ if (column1Based !== void 0) {
152
+ const colIndex = Math.min(column1Based - 1, targetLine.length);
153
+ index += colIndex;
154
+ } else {
155
+ index = lineEnd;
156
+ }
157
+ return { index, lineEnd, lineStart };
158
+ }
159
+ function prepareContentForInjection(pieces, commentsMode, ext, createNewLine) {
160
+ let preparedPieces = pieces.slice();
161
+ if (commentsMode?.activate) {
162
+ preparedPieces = applyCommentWrapping(preparedPieces, commentsMode, ext);
163
+ }
164
+ let result = preparedPieces.join("\n");
165
+ if (createNewLine && result) {
166
+ result = "\n" + result;
167
+ }
168
+ return result;
169
+ }
170
+ function applyCommentWrapping(pieces, commentsMode, ext) {
171
+ const { lineComment, blockStart, blockEnd } = getCommentSymbols(ext);
172
+ if (pieces.length === 1) {
173
+ return pieces.map((p) => ` ${lineComment} ${p}`);
174
+ }
175
+ const isJsdoc = commentsMode.useJsdocForMultiline;
176
+ const processedPieces = pieces.map((p) => p === "" ? " " : p);
177
+ return [
178
+ ` ${blockStart}${isJsdoc ? "*" : ""} ${processedPieces[0]}`,
179
+ ...processedPieces.slice(1, -1).map((p) => ` ${isJsdoc ? "*" : ""} ${p}`),
180
+ ` ${isJsdoc ? "*" : ""} ${processedPieces[processedPieces.length - 1]} ${blockEnd}`
181
+ ];
182
+ }
183
+ function injectWithMagicString(magicString, originalContent, line1Based, column1Based, preparedContent, createNewLine) {
184
+ try {
185
+ const { index, lineEnd, lineStart } = getCharacterIndex(
186
+ originalContent,
187
+ line1Based,
188
+ column1Based
189
+ );
190
+ const currentLength = originalContent.length;
191
+ if (index > currentLength) {
192
+ const linesNeeded = Math.ceil((index - currentLength) / 50);
193
+ const extension = "\n".repeat(linesNeeded);
194
+ magicString.append(extension);
195
+ }
196
+ if (createNewLine) {
197
+ if (column1Based !== void 0) {
198
+ const currentLineContent = originalContent.split(/\r?\n/)[line1Based - 1] || "";
199
+ const beforeColumn = currentLineContent.slice(0, column1Based - 1);
200
+ const afterColumn = currentLineContent.slice(column1Based - 1);
201
+ const replacement = beforeColumn + "\n" + preparedContent + (afterColumn ? "\n" + afterColumn : "");
202
+ magicString.overwrite(lineStart, lineEnd, replacement);
203
+ } else {
204
+ magicString.appendRight(lineEnd, preparedContent);
205
+ }
206
+ } else {
207
+ magicString.appendRight(index, preparedContent);
208
+ }
209
+ return true;
210
+ } catch (err) {
211
+ console.error(`[injector] MagicString injection failed: ${err.message}`);
212
+ return false;
213
+ }
214
+ }
215
+ function getCommentSymbols(ext) {
216
+ const map = {
217
+ ".js": { line: "//", blockStart: "/*", blockEnd: "*/" },
218
+ ".ts": { line: "//", blockStart: "/*", blockEnd: "*/" },
219
+ ".jsx": { line: "//", blockStart: "/*", blockEnd: "*/" },
220
+ ".tsx": { line: "//", blockStart: "/*", blockEnd: "*/" },
221
+ ".css": { line: "//", blockStart: "/*", blockEnd: "*/" },
222
+ ".scss": { line: "//", blockStart: "/*", blockEnd: "*/" },
223
+ ".html": { line: "//", blockStart: "<!--", blockEnd: "-->" },
224
+ ".py": { line: "#", blockStart: '"""', blockEnd: '"""' },
225
+ ".sh": { line: "#", blockStart: ": <<'BLOCK'", blockEnd: "BLOCK" },
226
+ ".yaml": { line: "#", blockStart: "# ---", blockEnd: "# ---" },
227
+ ".yml": { line: "#", blockStart: "# ---", blockEnd: "# ---" },
228
+ ".json": { line: "//", blockStart: "/*", blockEnd: "*/" },
229
+ ".jsonc": { line: "//", blockStart: "/*", blockEnd: "*/" }
230
+ };
231
+ const symbols = map[ext.toLowerCase()] ?? { line: "//", blockStart: "/*", blockEnd: "*/" };
232
+ return {
233
+ lineComment: symbols.line ?? "//",
234
+ blockStart: symbols.blockStart,
235
+ blockEnd: symbols.blockEnd
236
+ };
237
+ }
@@ -1,5 +1,5 @@
1
1
  import { endPrompt, startPrompt } from "@reliverse/rempts";
2
- const version = "1.7.22";
2
+ const version = "1.7.23";
3
3
  export async function showStartPrompt(isDev) {
4
4
  await startPrompt({
5
5
  titleColor: "inverse",