isaacscript 3.16.0 → 3.16.1-dev.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.
Files changed (214) hide show
  1. package/dist/checkForWindowsTerminalBugs.js +57 -0
  2. package/{src → dist}/classes/Config.js +1 -6
  3. package/dist/classes/ValidatedConfig.js +1 -0
  4. package/{src → dist}/commands/check/check.js +60 -68
  5. package/dist/commands/check/check.test.js +86 -0
  6. package/dist/commands/copy/copy.js +29 -0
  7. package/dist/commands/init/checkIfProjectPathExists.js +21 -0
  8. package/dist/commands/init/checkModSubdirectory.js +9 -0
  9. package/dist/commands/init/checkModTargetDirectory.js +24 -0
  10. package/dist/commands/init/createProject.js +199 -0
  11. package/dist/commands/init/getAuthorName.js +22 -0
  12. package/dist/commands/init/getModsDir.js +48 -0
  13. package/dist/commands/init/getProjectPath.js +84 -0
  14. package/dist/commands/init/init.js +73 -0
  15. package/dist/commands/init/installVSCodeExtensions.js +27 -0
  16. package/dist/commands/init/promptSaveSlot.js +17 -0
  17. package/{src → dist}/commands/init/promptVSCode.js +5 -10
  18. package/dist/commands/monitor/copyWatcherMod.js +31 -0
  19. package/dist/commands/monitor/modDirectorySyncer/modDirectorySyncer.js +44 -0
  20. package/dist/commands/monitor/monitor.js +154 -0
  21. package/{src → dist}/commands/monitor/notifyGame.js +14 -22
  22. package/{src → dist}/commands/monitor/saveDatWriter/saveDatWriter.js +24 -27
  23. package/dist/commands/monitor/saveDatWriter/types.js +1 -0
  24. package/dist/commands/monitor/spawnModDirectorySyncer.js +31 -0
  25. package/dist/commands/monitor/spawnSaveDatWriter.js +29 -0
  26. package/dist/commands/monitor/spawnTSTLWatcher.js +74 -0
  27. package/dist/commands/monitor/touchWatcherSaveDatFiles.js +15 -0
  28. package/dist/commands/publish/isaacscriptMod.js +90 -0
  29. package/dist/commands/publish/prePublish.js +105 -0
  30. package/dist/commands/publish/publish.js +39 -0
  31. package/dist/commands/publish/validate.js +46 -0
  32. package/dist/common.js +65 -0
  33. package/dist/configFile.js +54 -0
  34. package/dist/constants.js +61 -0
  35. package/dist/customStage.js +298 -0
  36. package/dist/dev.js +10 -0
  37. package/{src/enums → dist/enums/copied}/DoorSlot.js +5 -6
  38. package/dist/enums/copied/DoorSlotFlag.js +35 -0
  39. package/{src/enums → dist/enums/copied}/RoomShape.js +5 -6
  40. package/{src → dist}/exec.js +19 -28
  41. package/dist/git.js +133 -0
  42. package/dist/interfaces/GitHubCLIHostsYAML.js +1 -0
  43. package/dist/interfaces/IsaacScriptTSConfig.js +1 -0
  44. package/dist/interfaces/ShadersXML.js +1 -0
  45. package/dist/interfaces/copied/CustomStageTSConfig.js +4 -0
  46. package/dist/interfaces/copied/JSONRoomsFile.js +4 -0
  47. package/dist/main.js +132 -0
  48. package/dist/objects/copied/doorSlotToDoorSlotFlag.js +16 -0
  49. package/dist/objects/copied/roomShapeToDoorSlotCoordinates.js +112 -0
  50. package/dist/packageManager.js +68 -0
  51. package/{src → dist}/parseArgs.js +36 -15
  52. package/dist/prompt.js +79 -0
  53. package/dist/tsconfig.js +98 -0
  54. package/dist/types/Command.js +4 -0
  55. package/dist/utils.js +8 -0
  56. package/dist/validateInIsaacScriptProject.js +29 -0
  57. package/dist/validateNodeVersion.js +20 -0
  58. package/file-templates/dynamic/.github/workflows/setup/action.yml +21 -0
  59. package/file-templates/dynamic/README.mod.md +4 -3
  60. package/file-templates/dynamic/_gitignore +0 -2
  61. package/file-templates/dynamic/package.mod.json +8 -0
  62. package/file-templates/dynamic/package.ts.json +5 -1
  63. package/file-templates/{dynamic → static}/.github/workflows/ci.yml +10 -38
  64. package/file-templates/static/scripts/tsconfig.json +28 -0
  65. package/file-templates/static-mod/.vscode/settings.json +1 -1
  66. package/file-templates/static-mod/{_cspell.json → _cspell.jsonc} +2 -2
  67. package/file-templates/static-mod/_eslintrc.cjs +1 -1
  68. package/file-templates/static-mod/prettier.config.mjs +6 -1
  69. package/file-templates/static-mod/scripts/lint.ts +36 -0
  70. package/file-templates/static-mod/tsconfig.json +5 -5
  71. package/file-templates/static-ts/.vscode/settings.json +1 -1
  72. package/file-templates/static-ts/{_cspell.json → _cspell.jsonc} +5 -5
  73. package/file-templates/static-ts/_eslintrc.cjs +1 -1
  74. package/file-templates/static-ts/knip.jsonc +6 -0
  75. package/file-templates/static-ts/prettier.config.mjs +6 -1
  76. package/file-templates/static-ts/scripts/lint.ts +30 -0
  77. package/file-templates/static-ts/tsconfig.json +3 -3
  78. package/package.json +27 -9
  79. package/plugins/addCrashDebugStatements.cjs +56 -0
  80. package/plugins/addCrashDebugStatements.ts +39 -0
  81. package/{src/plugins/addIsaacScriptCommentHeader.js → plugins/addIsaacScriptCommentHeader.cjs} +1 -1
  82. package/plugins/addIsaacScriptCommentHeader.ts +42 -0
  83. package/{src/plugins/addIsaacScriptCommentHeaderCommon.js → plugins/addIsaacScriptCommentHeaderCommon.cjs} +6 -4
  84. package/plugins/addIsaacScriptCommentHeaderCommon.ts +59 -0
  85. package/{src/plugins/noExtendedEnums.js → plugins/noExtendedEnums.cjs} +25 -4
  86. package/plugins/noExtendedEnums.ts +83 -0
  87. package/plugins/tsconfig.json +28 -0
  88. package/schemas/tsconfig-isaacscript-section-schema.json +1 -0
  89. package/file-templates/static/nuke.sh +0 -53
  90. package/file-templates/static/update.sh +0 -26
  91. package/file-templates/static-mod/build.sh +0 -22
  92. package/file-templates/static-mod/lint.sh +0 -43
  93. package/file-templates/static-mod/publish.sh +0 -10
  94. package/file-templates/static-mod/run.sh +0 -10
  95. package/file-templates/static-ts/build.sh +0 -53
  96. package/file-templates/static-ts/lint.sh +0 -35
  97. package/file-templates/static-ts/publish.sh +0 -10
  98. package/file-templates/static-ts/run.sh +0 -42
  99. package/src/checkForWindowsTerminalBugs.js +0 -71
  100. package/src/checkForWindowsTerminalBugs.js.map +0 -1
  101. package/src/classes/Config.js.map +0 -1
  102. package/src/classes/ValidatedConfig.js +0 -3
  103. package/src/classes/ValidatedConfig.js.map +0 -1
  104. package/src/commands/check/check.js.map +0 -1
  105. package/src/commands/check/check.test.js +0 -87
  106. package/src/commands/check/check.test.js.map +0 -1
  107. package/src/commands/copy/copy.js +0 -38
  108. package/src/commands/copy/copy.js.map +0 -1
  109. package/src/commands/init/checkIfProjectPathExists.js +0 -28
  110. package/src/commands/init/checkIfProjectPathExists.js.map +0 -1
  111. package/src/commands/init/checkModSubdirectory.js +0 -16
  112. package/src/commands/init/checkModSubdirectory.js.map +0 -1
  113. package/src/commands/init/checkModTargetDirectory.js +0 -31
  114. package/src/commands/init/checkModTargetDirectory.js.map +0 -1
  115. package/src/commands/init/createProject.js +0 -220
  116. package/src/commands/init/createProject.js.map +0 -1
  117. package/src/commands/init/getAuthorName.js +0 -27
  118. package/src/commands/init/getAuthorName.js.map +0 -1
  119. package/src/commands/init/getModsDir.js +0 -58
  120. package/src/commands/init/getModsDir.js.map +0 -1
  121. package/src/commands/init/getProjectPath.js +0 -89
  122. package/src/commands/init/getProjectPath.js.map +0 -1
  123. package/src/commands/init/init.js +0 -78
  124. package/src/commands/init/init.js.map +0 -1
  125. package/src/commands/init/installVSCodeExtensions.js +0 -35
  126. package/src/commands/init/installVSCodeExtensions.js.map +0 -1
  127. package/src/commands/init/promptSaveSlot.js +0 -22
  128. package/src/commands/init/promptSaveSlot.js.map +0 -1
  129. package/src/commands/init/promptVSCode.js.map +0 -1
  130. package/src/commands/monitor/copyWatcherMod.js +0 -39
  131. package/src/commands/monitor/copyWatcherMod.js.map +0 -1
  132. package/src/commands/monitor/modDirectorySyncer/modDirectorySyncer.js +0 -47
  133. package/src/commands/monitor/modDirectorySyncer/modDirectorySyncer.js.map +0 -1
  134. package/src/commands/monitor/monitor.js +0 -263
  135. package/src/commands/monitor/monitor.js.map +0 -1
  136. package/src/commands/monitor/notifyGame.js.map +0 -1
  137. package/src/commands/monitor/saveDatWriter/saveDatWriter.js.map +0 -1
  138. package/src/commands/monitor/saveDatWriter/types.js +0 -3
  139. package/src/commands/monitor/saveDatWriter/types.js.map +0 -1
  140. package/src/commands/monitor/spawnSaveDatWriter.js +0 -35
  141. package/src/commands/monitor/spawnSaveDatWriter.js.map +0 -1
  142. package/src/commands/monitor/touchWatcherSaveDatFiles.js +0 -21
  143. package/src/commands/monitor/touchWatcherSaveDatFiles.js.map +0 -1
  144. package/src/commands/publish/isaacscriptMod.js +0 -97
  145. package/src/commands/publish/isaacscriptMod.js.map +0 -1
  146. package/src/commands/publish/prePublish.js +0 -120
  147. package/src/commands/publish/prePublish.js.map +0 -1
  148. package/src/commands/publish/publish.js +0 -42
  149. package/src/commands/publish/publish.js.map +0 -1
  150. package/src/commands/publish/validate.js +0 -58
  151. package/src/commands/publish/validate.js.map +0 -1
  152. package/src/common.js +0 -69
  153. package/src/common.js.map +0 -1
  154. package/src/configFile.js +0 -64
  155. package/src/configFile.js.map +0 -1
  156. package/src/constants.js +0 -67
  157. package/src/constants.js.map +0 -1
  158. package/src/customStage.js +0 -304
  159. package/src/customStage.js.map +0 -1
  160. package/src/dev.js +0 -19
  161. package/src/dev.js.map +0 -1
  162. package/src/enums/DoorSlot.js.map +0 -1
  163. package/src/enums/DoorSlotFlag.js +0 -37
  164. package/src/enums/DoorSlotFlag.js.map +0 -1
  165. package/src/enums/PackageManager.js +0 -10
  166. package/src/enums/PackageManager.js.map +0 -1
  167. package/src/enums/RoomShape.js.map +0 -1
  168. package/src/exec.js.map +0 -1
  169. package/src/file.js +0 -247
  170. package/src/file.js.map +0 -1
  171. package/src/git.js +0 -154
  172. package/src/git.js.map +0 -1
  173. package/src/interfaces/GitHubCLIHostsYAML.js +0 -3
  174. package/src/interfaces/GitHubCLIHostsYAML.js.map +0 -1
  175. package/src/interfaces/IsaacScriptTSConfig.js +0 -3
  176. package/src/interfaces/IsaacScriptTSConfig.js.map +0 -1
  177. package/src/interfaces/ShadersXML.js +0 -3
  178. package/src/interfaces/ShadersXML.js.map +0 -1
  179. package/src/interfaces/copied/CustomStageTSConfig.js +0 -4
  180. package/src/interfaces/copied/CustomStageTSConfig.js.map +0 -1
  181. package/src/interfaces/copied/JSONRoomsFile.js +0 -4
  182. package/src/interfaces/copied/JSONRoomsFile.js.map +0 -1
  183. package/src/isaacScriptCommonTS.js +0 -233
  184. package/src/isaacScriptCommonTS.js.map +0 -1
  185. package/src/json.js +0 -42
  186. package/src/json.js.map +0 -1
  187. package/src/main.js +0 -127
  188. package/src/main.js.map +0 -1
  189. package/src/objects/doorSlotToDoorSlotFlag.js +0 -17
  190. package/src/objects/doorSlotToDoorSlotFlag.js.map +0 -1
  191. package/src/objects/roomShapeDoorToSlotCoordinates.js +0 -115
  192. package/src/objects/roomShapeDoorToSlotCoordinates.js.map +0 -1
  193. package/src/packageManager.js +0 -153
  194. package/src/packageManager.js.map +0 -1
  195. package/src/parseArgs.js.map +0 -1
  196. package/src/plugins/addCrashDebugStatements.js +0 -32
  197. package/src/plugins/addCrashDebugStatements.js.map +0 -1
  198. package/src/plugins/addIsaacScriptCommentHeader.js.map +0 -1
  199. package/src/plugins/addIsaacScriptCommentHeaderCommon.js.map +0 -1
  200. package/src/plugins/noExtendedEnums.js.map +0 -1
  201. package/src/prompt.js +0 -87
  202. package/src/prompt.js.map +0 -1
  203. package/src/tsconfig.js +0 -103
  204. package/src/tsconfig.js.map +0 -1
  205. package/src/types/Command.js +0 -9
  206. package/src/types/Command.js.map +0 -1
  207. package/src/utils.js +0 -15
  208. package/src/utils.js.map +0 -1
  209. package/src/validateInIsaacScriptProject.js +0 -36
  210. package/src/validateInIsaacScriptProject.js.map +0 -1
  211. package/src/validateNodeVersion.js +0 -29
  212. package/src/validateNodeVersion.js.map +0 -1
  213. package/src/version.js +0 -20
  214. package/src/version.js.map +0 -1
@@ -0,0 +1,199 @@
1
+ import chalk from "chalk";
2
+ import { PACKAGE_JSON, PackageManager, copyFileOrDirectory, getFileNamesInDirectory, getPackageManagerExecCommand, getPackageManagerInstallCICommand, getPackageManagerInstallCommand, getPackageManagerLockFileName, isFile, makeDirectory, readFile, renameFile, updatePackageJSON, writeFile, } from "isaacscript-common-node";
3
+ import { removeLinesBetweenMarkers, removeLinesMatching, repeat, } from "isaacscript-common-ts";
4
+ import path from "node:path";
5
+ import { Config } from "../../classes/Config.js";
6
+ import { createConfigFile } from "../../configFile.js";
7
+ import { ACTION_YML, ACTION_YML_TEMPLATE_PATH, GITIGNORE_TEMPLATE_PATH, MAIN_DEV_TS_TEMPLATE_PATH, MAIN_TS, MAIN_TS_TEMPLATE_PATH, METADATA_XML, METADATA_XML_TEMPLATE_PATH, README_MD, TEMPLATES_DIR, TEMPLATES_DYNAMIC_DIR, TEMPLATES_STATIC_DIR, } from "../../constants.js";
8
+ import { execShell, execShellString } from "../../exec.js";
9
+ import { initGitRepository } from "../../git.js";
10
+ export async function createProject(projectName, authorName, projectPath, createNewDir, modsDirectory, saveSlot, gitRemoteURL, skipInstall, packageManager, typeScript, dev, verbose) {
11
+ if (createNewDir) {
12
+ makeDirectory(projectPath);
13
+ }
14
+ const config = new Config(modsDirectory, saveSlot, dev);
15
+ createConfigFile(projectPath, config, typeScript);
16
+ copyStaticFiles(projectPath, typeScript);
17
+ copyDynamicFiles(projectName, authorName, projectPath, packageManager, dev, typeScript);
18
+ upgradeYarn(projectPath, packageManager, verbose);
19
+ await updatePackageJSON(projectPath);
20
+ installNodeModules(projectPath, skipInstall, packageManager, verbose);
21
+ formatFiles(projectPath, packageManager, verbose);
22
+ // Only make the initial commit once all of the files have been copied and formatted.
23
+ initGitRepository(projectPath, gitRemoteURL, verbose);
24
+ const noun = typeScript ? "project" : "mod";
25
+ console.log(`Successfully created ${noun}: ${chalk.green(projectName)}`);
26
+ }
27
+ /** Copy static files, like ".eslintrc.cjs", "tsconfig.json", etc. */
28
+ function copyStaticFiles(projectPath, typeScript) {
29
+ // First, copy the static files that are shared between TypeScript projects and IsaacScript mods.
30
+ copyTemplateDirectoryWithoutOverwriting(TEMPLATES_STATIC_DIR, projectPath);
31
+ // Second, copy the files that are specific to either a TypeScript project or an IsaacScript mod.
32
+ const staticDirSuffix = typeScript ? "ts" : "mod";
33
+ const staticDirPath = path.join(TEMPLATES_DIR, `static-${staticDirSuffix}`);
34
+ copyTemplateDirectoryWithoutOverwriting(staticDirPath, projectPath);
35
+ // Rename "_eslintrc.cjs" to ".eslintrc.cjs". (If it is kept as ".eslintrc.cjs", then local
36
+ // linting will fail.)
37
+ const ESLintConfigPath = path.join(projectPath, "_eslintrc.cjs");
38
+ const correctESLintConfigPath = path.join(projectPath, ".eslintrc.cjs");
39
+ renameFile(ESLintConfigPath, correctESLintConfigPath);
40
+ // Rename "_gitattributes" to ".gitattributes". (If it is kept as ".gitattributes", then it won't
41
+ // be committed to git.)
42
+ const gitAttributesPath = path.join(projectPath, "_gitattributes");
43
+ const correctGitAttributesPath = path.join(projectPath, ".gitattributes");
44
+ renameFile(gitAttributesPath, correctGitAttributesPath);
45
+ // Rename "_cspell.jsonc" to "cspell.jsonc". (If it is kept as "cspell.jsonc", then local spell
46
+ // checking will fail.)
47
+ const cSpellConfigPath = path.join(projectPath, "_cspell.jsonc");
48
+ const correctCSpellConfigPath = path.join(projectPath, "cspell.jsonc");
49
+ renameFile(cSpellConfigPath, correctCSpellConfigPath);
50
+ }
51
+ function copyTemplateDirectoryWithoutOverwriting(templateDirPath, projectPath) {
52
+ const fileNames = getFileNamesInDirectory(templateDirPath);
53
+ for (const fileName of fileNames) {
54
+ const templateFilePath = path.join(templateDirPath, fileName);
55
+ const destinationFilePath = path.join(projectPath, fileName);
56
+ if (!isFile(destinationFilePath)) {
57
+ copyFileOrDirectory(templateFilePath, destinationFilePath);
58
+ }
59
+ }
60
+ }
61
+ /** Copy files that need to have text replaced inside of them. */
62
+ function copyDynamicFiles(projectName, authorName, projectPath, packageManager, dev, typeScript) {
63
+ const workflowsPath = path.join(projectPath, ".github", "workflows");
64
+ makeDirectory(workflowsPath);
65
+ // `.github/workflows/setup/action.yml`
66
+ {
67
+ const fileName = ACTION_YML;
68
+ const templatePath = ACTION_YML_TEMPLATE_PATH;
69
+ const templateRaw = readFile(templatePath);
70
+ const template = parseTemplate(templateRaw, typeScript);
71
+ const lockFileName = getPackageManagerLockFileName(packageManager);
72
+ const installCommand = getPackageManagerInstallCICommand(packageManager);
73
+ const actionYML = template
74
+ .replaceAll("PACKAGE_MANAGER_NAME", packageManager)
75
+ .replaceAll("PACKAGE_MANAGER_LOCK_FILE_NAME", lockFileName)
76
+ .replaceAll("PACKAGE_MANAGER_INSTALL_COMMAND", installCommand);
77
+ const destinationPath = path.join(workflowsPath, fileName);
78
+ writeFile(destinationPath, actionYML);
79
+ }
80
+ // `.gitignore`
81
+ {
82
+ const templatePath = GITIGNORE_TEMPLATE_PATH;
83
+ const templateRaw = readFile(templatePath);
84
+ const template = parseTemplate(templateRaw, typeScript);
85
+ // Prepend a header with the project name.
86
+ let separatorLine = "# ";
87
+ repeat(projectName.length, () => {
88
+ separatorLine += "-";
89
+ });
90
+ separatorLine += "\n";
91
+ const gitIgnoreHeader = `${separatorLine}# ${projectName}\n${separatorLine}\n`;
92
+ const nodeGitIgnorePath = path.join(TEMPLATES_DYNAMIC_DIR, "Node.gitignore");
93
+ const nodeGitIgnore = readFile(nodeGitIgnorePath);
94
+ // eslint-disable-next-line prefer-template
95
+ const gitignore = gitIgnoreHeader + template + "\n" + nodeGitIgnore;
96
+ // We need to replace the underscore with a period.
97
+ const destinationPath = path.join(projectPath, ".gitignore");
98
+ writeFile(destinationPath, gitignore);
99
+ }
100
+ // `package.json`
101
+ {
102
+ // There are two versions of the template, one for TypeScript, and one for IsaacScript mods.
103
+ const packageJSONTemplateFileName = typeScript
104
+ ? "package.ts.json"
105
+ : "package.mod.json";
106
+ const templatePath = path.join(TEMPLATES_DYNAMIC_DIR, packageJSONTemplateFileName);
107
+ const template = readFile(templatePath);
108
+ const packageJSON = template
109
+ .replaceAll("PROJECT_NAME", projectName)
110
+ .replaceAll("AUTHOR_NAME", authorName ?? "unknown")
111
+ .replaceAll("PACKAGE_MANAGER", packageManager);
112
+ const destinationPath = path.join(projectPath, PACKAGE_JSON);
113
+ writeFile(destinationPath, packageJSON);
114
+ }
115
+ // `README.md`
116
+ {
117
+ // There are two versions of the template, one for TypeScript, and one for IsaacScript mods.
118
+ const readmeMDTemplateFileName = typeScript
119
+ ? "README.ts.md"
120
+ : "README.mod.md";
121
+ const templatePath = path.join(TEMPLATES_DYNAMIC_DIR, readmeMDTemplateFileName);
122
+ const template = readFile(templatePath);
123
+ // "PROJECT-NAME" must be hyphenated, as using an underscore will break Prettier for some
124
+ // reason.
125
+ const readmeMD = template.replaceAll("PROJECT-NAME", projectName);
126
+ const destinationPath = path.join(projectPath, README_MD);
127
+ writeFile(destinationPath, readmeMD);
128
+ }
129
+ // `mod/metadata.xml`
130
+ if (!typeScript) {
131
+ const modPath = path.join(projectPath, "mod");
132
+ makeDirectory(modPath);
133
+ const fileName = METADATA_XML;
134
+ const templatePath = METADATA_XML_TEMPLATE_PATH;
135
+ const template = readFile(templatePath);
136
+ const metadataXML = template.replaceAll("PROJECT_NAME", projectName);
137
+ const destinationPath = path.join(modPath, fileName);
138
+ writeFile(destinationPath, metadataXML);
139
+ }
140
+ const srcPath = path.join(projectPath, "src");
141
+ makeDirectory(srcPath);
142
+ // `src/main.ts` (TypeScript projects use the simple version from the "static-ts" directory.)
143
+ if (!typeScript) {
144
+ // Convert snake_case and kebab-case to camelCase. (Kebab-case in particular will make the
145
+ // example TypeScript file fail to compile.)
146
+ const fileName = MAIN_TS;
147
+ const templatePath = MAIN_TS_TEMPLATE_PATH;
148
+ const template = readFile(templatePath);
149
+ const mainTS = template.replaceAll("PROJECT_NAME", projectName);
150
+ const destinationPath = path.join(srcPath, fileName);
151
+ writeFile(destinationPath, mainTS);
152
+ }
153
+ // If we are initializing an IsaacScript project intended to be used for development, we can
154
+ // include a better starter file.
155
+ if (!typeScript && dev) {
156
+ const fileName = MAIN_TS;
157
+ const templatePath = MAIN_DEV_TS_TEMPLATE_PATH;
158
+ const template = readFile(templatePath);
159
+ const mainTS = template; // No replacements are necessary for this file.
160
+ const destinationPath = path.join(srcPath, fileName);
161
+ writeFile(destinationPath, mainTS);
162
+ }
163
+ }
164
+ function parseTemplate(template, typeScript) {
165
+ const otherTemplateKind = typeScript ? "mod" : "ts";
166
+ const otherTemplateMarker = `@template-${otherTemplateKind}`;
167
+ const templateWithoutMarkers = removeLinesBetweenMarkers(template, otherTemplateMarker);
168
+ const templateWithoutTemplateComments = removeLinesMatching(templateWithoutMarkers, "@template");
169
+ const templateWithoutMultipleLineBreaks = templateWithoutTemplateComments.replaceAll(/\n\s*\n\s*\n/g, "\n\n");
170
+ return templateWithoutMultipleLineBreaks;
171
+ }
172
+ /**
173
+ * If we are using yarn, assume that we want to use the latest version, which requires some
174
+ * additional commands to be performed.
175
+ */
176
+ function upgradeYarn(projectPath, packageManager, verbose) {
177
+ if (packageManager !== PackageManager.yarn) {
178
+ return;
179
+ }
180
+ execShell("yarn", ["set", "version", "latest"], verbose, false, projectPath);
181
+ // https://yarnpkg.com/features/linkers
182
+ // - `pnp` is the default, but it requires a VSCode extension.
183
+ // - `pnpm` is fast, but it does not work with a D drive:
184
+ // https://github.com/yarnpkg/berry/issues/5326
185
+ // - Thus, we use `node-modules`.
186
+ execShell("yarn", ["config", "set", "nodeLinker", "node-modules"], verbose, false, projectPath);
187
+ }
188
+ function installNodeModules(projectPath, skipInstall, packageManager, verbose) {
189
+ if (skipInstall) {
190
+ return;
191
+ }
192
+ const command = getPackageManagerInstallCommand(packageManager);
193
+ console.log(`Installing node modules with "${command}"... (This can take a long time.)`);
194
+ execShellString(command, verbose, false, projectPath);
195
+ }
196
+ function formatFiles(projectPath, packageManager, verbose) {
197
+ const command = getPackageManagerExecCommand(packageManager);
198
+ execShell(command, ["prettier", "--write", projectPath], verbose, false, projectPath);
199
+ }
@@ -0,0 +1,22 @@
1
+ import { fatalError } from "isaacscript-common-node";
2
+ import { getGitHubUsername } from "../../git.js";
3
+ import { getInputString } from "../../prompt.js";
4
+ export async function getAuthorName(typeScript) {
5
+ const gitHubUsername = getGitHubUsername();
6
+ if (gitHubUsername !== undefined) {
7
+ return gitHubUsername;
8
+ }
9
+ if (!typeScript) {
10
+ // In IsaacScript mods, putting the author in the "package.json" file is not necessary.
11
+ return undefined;
12
+ }
13
+ return getNewAuthorName();
14
+ }
15
+ async function getNewAuthorName() {
16
+ console.log("The author name was not found from the GitHub CLI configuration file.");
17
+ const authorName = await getInputString("Enter the author of the project:");
18
+ if (authorName === "") {
19
+ fatalError("You must enter an author name.");
20
+ }
21
+ return authorName;
22
+ }
@@ -0,0 +1,48 @@
1
+ import chalk from "chalk";
2
+ import { fatalError, isDirectory } from "isaacscript-common-node";
3
+ import path from "node:path";
4
+ import { HOME_DIR } from "../../constants.js";
5
+ import { getInputString } from "../../prompt.js";
6
+ const MODS = "mods";
7
+ const DEFAULT_MODS_PATH_WINDOWS = path.join("C:", "Program Files (x86)", "Steam", "steamapps", "common", "The Binding of Isaac Rebirth", MODS);
8
+ /** This is a subdirectory of `$HOME`. */
9
+ const DEFAULT_MODS_PATH_LINUX = path.join(HOME_DIR, ".local", "share", "Steam", "steamapps", "common", "The Binding of Isaac Rebirth", MODS);
10
+ export async function getModsDir(args, typeScript) {
11
+ if (typeScript) {
12
+ return undefined;
13
+ }
14
+ if (args.modsDirectory !== undefined) {
15
+ // They specified the "--mods-directory" command-line flag, so there is no need to prompt the
16
+ // user for it.
17
+ return args.modsDirectory;
18
+ }
19
+ const defaultModsPath = getDefaultModsPath(process.platform);
20
+ if (isDirectory(defaultModsPath)) {
21
+ return defaultModsPath;
22
+ }
23
+ console.error(`Failed to find your mods directory at: ${chalk.green(defaultModsPath)}`);
24
+ const modsDir = await getInputString(`Enter the full path to the "${MODS}" directory on your system, which should be next to your "isaac-ng.exe" program:`);
25
+ if (modsDir === "") {
26
+ fatalError("Error: You did not provide a response; exiting.");
27
+ }
28
+ if (!isDirectory(modsDir)) {
29
+ fatalError(`Error: The directory of "${chalk.green(modsDir)}" does not exist. Exiting.`);
30
+ }
31
+ if (path.basename(modsDir) !== MODS) {
32
+ fatalError(`Error: You entered a path of "${chalk.green(modsDir)}", but you need to input a directory with a name of "${MODS}" at the end. Exiting.`);
33
+ }
34
+ return modsDir;
35
+ }
36
+ function getDefaultModsPath(platform) {
37
+ switch (platform) {
38
+ case "win32": {
39
+ return DEFAULT_MODS_PATH_WINDOWS;
40
+ }
41
+ case "linux": {
42
+ return DEFAULT_MODS_PATH_LINUX;
43
+ }
44
+ default: {
45
+ return fatalError(`There does not exist a default mod path for the platform of: ${chalk.green(platform)}`);
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,84 @@
1
+ import chalk from "chalk";
2
+ import { fatalError } from "isaacscript-common-node";
3
+ import { hasWhiteSpace, isKebabCase } from "isaacscript-common-ts";
4
+ import path from "node:path";
5
+ import { CURRENT_DIRECTORY_NAME, CWD } from "../../constants.js";
6
+ import { getInputString, getInputYesNo } from "../../prompt.js";
7
+ // From: https://gist.github.com/doctaphred/d01d05291546186941e1b7ddc02034d3
8
+ const ILLEGAL_CHARACTERS_FOR_WINDOWS_FILENAMES = [
9
+ "<",
10
+ ">",
11
+ ":",
12
+ '"',
13
+ "/",
14
+ "\\",
15
+ "|",
16
+ "?",
17
+ "*",
18
+ ];
19
+ export async function getProjectPath(args, useCurrentDir, yes, forceName) {
20
+ let projectName = getProjectNameFromCommandLineArgument(args);
21
+ let projectPath;
22
+ let createNewDir;
23
+ if (useCurrentDir) {
24
+ // The "--use-current-dir" command-line flag was specified, so there is no need to prompt the
25
+ // user.
26
+ projectName = CURRENT_DIRECTORY_NAME;
27
+ projectPath = CWD;
28
+ createNewDir = false;
29
+ }
30
+ else if (projectName !== undefined) {
31
+ // The project name was specified on the command-line.
32
+ projectPath = path.join(CWD, projectName);
33
+ createNewDir = true;
34
+ }
35
+ else if (yes) {
36
+ // The "--yes" command-line flag was specified and the project name was not specified on the
37
+ // command-line, so default to using the current directory.
38
+ projectName = CURRENT_DIRECTORY_NAME;
39
+ projectPath = CWD;
40
+ createNewDir = false;
41
+ }
42
+ else {
43
+ // The project name was not specified on the command-line, so prompt the user for it.
44
+ [projectName, projectPath, createNewDir] = await getNewProjectName();
45
+ }
46
+ validateProjectName(projectName, forceName);
47
+ console.log(`Using a project name of: ${chalk.green(projectName)}`);
48
+ return [projectPath, createNewDir];
49
+ }
50
+ function getProjectNameFromCommandLineArgument(args) {
51
+ const { name } = args;
52
+ return typeof name === "string" && name !== "" ? name : undefined;
53
+ }
54
+ async function getNewProjectName() {
55
+ console.log("You did not specify a project name as a command-line argument.");
56
+ const shouldUseCurrentDir = await getInputYesNo(`Would you like to create a new project using the current directory "${chalk.green(CURRENT_DIRECTORY_NAME)}" as the root?`);
57
+ if (shouldUseCurrentDir) {
58
+ return [CURRENT_DIRECTORY_NAME, CWD, false];
59
+ }
60
+ const projectName = await getInputString("Enter the name of the project:");
61
+ const projectPath = path.join(CWD, projectName);
62
+ return [projectName, projectPath, true];
63
+ }
64
+ function validateProjectName(projectName, forceName) {
65
+ if (projectName === "") {
66
+ fatalError("Error: You cannot have a blank project name.");
67
+ }
68
+ if (process.platform === "win32") {
69
+ for (const character of ILLEGAL_CHARACTERS_FOR_WINDOWS_FILENAMES) {
70
+ if (projectName.includes(character)) {
71
+ fatalError(`Error: The "${character}" character is not allowed in a Windows file name.`);
72
+ }
73
+ }
74
+ }
75
+ if (forceName) {
76
+ return;
77
+ }
78
+ if (hasWhiteSpace(projectName)) {
79
+ fatalError('Error: The project name has whitespace in it, which is not allowed. Use kebab-case for your project name. (e.g. "green-candle")');
80
+ }
81
+ if (!isKebabCase(projectName)) {
82
+ fatalError('Error: The project name is not in kebab-case. (Kebab-case is the style of using all lowercase letters, with words separated by hyphens.) Project names must use kebab-case to match GitHub repository standards. If necessary, you can override this check with the "--force-name" flag.');
83
+ }
84
+ }
@@ -0,0 +1,73 @@
1
+ import chalk from "chalk";
2
+ import commandExists from "command-exists";
3
+ import { getPackageManagerExecCommand } from "isaacscript-common-node";
4
+ import path from "node:path";
5
+ import { CWD, PROJECT_NAME } from "../../constants.js";
6
+ import { promptGitHubRepoOrGitRemoteURL } from "../../git.js";
7
+ import { getPackageManagerUsedForNewProject } from "../../packageManager.js";
8
+ import { checkIfProjectPathExists } from "./checkIfProjectPathExists.js";
9
+ import { checkModSubdirectory } from "./checkModSubdirectory.js";
10
+ import { checkModTargetDirectory } from "./checkModTargetDirectory.js";
11
+ import { createProject } from "./createProject.js";
12
+ import { getAuthorName } from "./getAuthorName.js";
13
+ import { getModsDir } from "./getModsDir.js";
14
+ import { getProjectPath } from "./getProjectPath.js";
15
+ import { installVSCodeExtensions } from "./installVSCodeExtensions.js";
16
+ import { promptSaveSlot } from "./promptSaveSlot.js";
17
+ import { promptVSCode } from "./promptVSCode.js";
18
+ export async function init(args, typeScript) {
19
+ const packageManager = getPackageManagerUsedForNewProject(args);
20
+ const noGit = args.noGit === true;
21
+ const skipInstall = args.skipInstall === true;
22
+ const useCurrentDir = args.useCurrentDir === true;
23
+ const verbose = args.verbose === true;
24
+ const vscode = args.vscode === true;
25
+ const yes = args.yes === true;
26
+ const forceName = args.forceName === true;
27
+ const dev = args.dev === true;
28
+ // Prompt the end-user for some information (and validate it as we go).
29
+ const [projectPath, createNewDir] = await getProjectPath(args, useCurrentDir, yes, forceName);
30
+ await checkIfProjectPathExists(projectPath, yes);
31
+ const projectName = path.basename(projectPath);
32
+ const authorName = await getAuthorName(typeScript);
33
+ const gitRemoteURL = await promptGitHubRepoOrGitRemoteURL(projectName, noGit, yes, dev, verbose);
34
+ const modsDirectory = await getModsDir(args, typeScript);
35
+ if (modsDirectory !== undefined) {
36
+ checkModSubdirectory(projectPath, modsDirectory);
37
+ await checkModTargetDirectory(modsDirectory, projectName, yes);
38
+ }
39
+ const saveSlot = typeScript ? undefined : await promptSaveSlot(args, yes);
40
+ // Now that we have asked the user all of the questions we need, we can create the project.
41
+ await createProject(projectName, authorName, projectPath, createNewDir, modsDirectory, saveSlot, gitRemoteURL, skipInstall, packageManager, typeScript, dev, verbose);
42
+ await openVSCode(projectPath, vscode, yes, verbose);
43
+ printFinishMessage(projectPath, projectName, packageManager, typeScript);
44
+ }
45
+ async function openVSCode(projectPath, vscode, yes, verbose) {
46
+ const VSCodeCommand = getVSCodeCommand();
47
+ if (VSCodeCommand === undefined) {
48
+ console.log('VSCode does not seem to be installed. (The "code" command is not in the path.) Skipping VSCode-related things.');
49
+ return;
50
+ }
51
+ installVSCodeExtensions(projectPath, VSCodeCommand, verbose);
52
+ await promptVSCode(projectPath, VSCodeCommand, vscode, yes, verbose);
53
+ }
54
+ function getVSCodeCommand() {
55
+ for (const VSCodeCommand of ["code", "codium", "code-oss", "code-insiders"]) {
56
+ if (commandExists.sync(VSCodeCommand)) {
57
+ return VSCodeCommand;
58
+ }
59
+ }
60
+ return undefined;
61
+ }
62
+ function printFinishMessage(projectPath, projectName, packageManager, typeScript) {
63
+ if (typeScript) {
64
+ return;
65
+ }
66
+ let commandsToType = "";
67
+ if (projectPath !== CWD) {
68
+ commandsToType += `"${chalk.green(`cd ${projectName}`)}" and `;
69
+ }
70
+ const command = getPackageManagerExecCommand(packageManager);
71
+ commandsToType += `"${chalk.green(`${command} isaacscript`)}"`;
72
+ console.log(`Now, start ${PROJECT_NAME} by typing ${commandsToType}.`);
73
+ }
@@ -0,0 +1,27 @@
1
+ import { fatalError, getJSONC, isFile } from "isaacscript-common-node";
2
+ import path from "node:path";
3
+ import { execShell } from "../../exec.js";
4
+ export function installVSCodeExtensions(projectPath, VSCodeCommand, verbose) {
5
+ // Installing extensions from inside WSL on Windows will result in the VSCode process never
6
+ // exiting for some reason. Thus, skip this step on Linux. (Linux users will probably be smart
7
+ // enough to install the extensions on their own.)
8
+ if (process.platform === "linux") {
9
+ return;
10
+ }
11
+ const extensions = getExtensionsFromJSON(projectPath);
12
+ for (const extensionName of extensions) {
13
+ execShell(VSCodeCommand, ["--install-extension", extensionName], verbose);
14
+ }
15
+ }
16
+ function getExtensionsFromJSON(projectPath) {
17
+ const extensionsJSONPath = path.join(projectPath, ".vscode", "extensions.json");
18
+ if (!isFile(extensionsJSONPath)) {
19
+ return [];
20
+ }
21
+ const extensionsJSON = getJSONC(extensionsJSONPath);
22
+ const { recommendations } = extensionsJSON;
23
+ if (!Array.isArray(recommendations)) {
24
+ fatalError('The "recommendations" field in the "extensions.json" file is not an array.');
25
+ }
26
+ return recommendations;
27
+ }
@@ -0,0 +1,17 @@
1
+ import { fatalError } from "isaacscript-common-node";
2
+ import { getInputInt } from "../../prompt.js";
3
+ export async function promptSaveSlot(args, yes) {
4
+ if (args.saveSlot !== undefined) {
5
+ // They specified the "--save-slot" command-line flag, so there is no need to prompt the user
6
+ // for it.
7
+ return args.saveSlot;
8
+ }
9
+ if (yes) {
10
+ return 1;
11
+ }
12
+ const saveSlot = await getInputInt("In-game, will you test your mod on save slot 1, 2, or 3?");
13
+ if (saveSlot !== 1 && saveSlot !== 2 && saveSlot !== 3) {
14
+ fatalError("Error: You must choose a number between 1 and 3.");
15
+ }
16
+ return saveSlot;
17
+ }
@@ -1,9 +1,6 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.promptVSCode = void 0;
4
- const exec_js_1 = require("../../exec.js");
5
- const prompt_js_1 = require("../../prompt.js");
6
- async function promptVSCode(projectPath, VSCodeCommand, vscode, yes, verbose) {
1
+ import { execShell } from "../../exec.js";
2
+ import { getInputYesNo } from "../../prompt.js";
3
+ export async function promptVSCode(projectPath, VSCodeCommand, vscode, yes, verbose) {
7
4
  if (vscode) {
8
5
  // They supplied the "--vscode" command-line flag, so there is no need to prompt the user.
9
6
  openVSCode(projectPath, VSCodeCommand, verbose);
@@ -18,13 +15,11 @@ async function promptVSCode(projectPath, VSCodeCommand, vscode, yes, verbose) {
18
15
  if (process.platform === "linux") {
19
16
  return;
20
17
  }
21
- const shouldOpenVSCode = await (0, prompt_js_1.getInputYesNo)("Do you want to open your new project in VSCode now?");
18
+ const shouldOpenVSCode = await getInputYesNo("Do you want to open your new project in VSCode now?");
22
19
  if (shouldOpenVSCode) {
23
20
  openVSCode(projectPath, VSCodeCommand, verbose);
24
21
  }
25
22
  }
26
- exports.promptVSCode = promptVSCode;
27
23
  function openVSCode(projectPath, VSCodeCommand, verbose) {
28
- (0, exec_js_1.execShell)(VSCodeCommand, [projectPath], verbose);
24
+ execShell(VSCodeCommand, [projectPath], verbose);
29
25
  }
30
- //# sourceMappingURL=promptVSCode.js.map
@@ -0,0 +1,31 @@
1
+ import { copyFileOrDirectory, deleteFileOrDirectory, isFile, readFile, writeFile, } from "isaacscript-common-node";
2
+ import path from "node:path";
3
+ import { DISABLE_IT_FILE, MAIN_LUA, WATCHER_MOD_NAME, WATCHER_MOD_SOURCE_PATH, } from "../../constants.js";
4
+ export function copyWatcherMod(config) {
5
+ // Check to see if this mod was disabled.
6
+ const watcherModPath = path.join(config.modsDirectory, WATCHER_MOD_NAME);
7
+ const disableItPath = path.join(watcherModPath, DISABLE_IT_FILE);
8
+ const watcherModDisabled = isFile(disableItPath);
9
+ // Delete and re-copy the watcher mod every time IsaacScript starts. This ensures that it is
10
+ // always the latest version.
11
+ deleteFileOrDirectory(watcherModPath);
12
+ copyFileOrDirectory(WATCHER_MOD_SOURCE_PATH, watcherModPath);
13
+ if (watcherModDisabled) {
14
+ // Since we deleted the directory, the "disable.it" file was deleted. Restore it.
15
+ writeFile(disableItPath, "");
16
+ }
17
+ // By default, the IsaacScript watcher mod automatically restarts the game, so we only need to
18
+ // disable it if the config option is explicitly set to false.
19
+ if (config.enableIsaacScriptWatcherAutoRestart === false) {
20
+ disableIsaacScriptWatcherAutomaticRestart(watcherModPath);
21
+ }
22
+ // If we copied a new version of the watcher mod into place, but the user currently has the game
23
+ // open, then the old version will stay loaded. However, if the watcher mod reloads itself, the
24
+ // game will crash, so there is no automated solution for this.
25
+ }
26
+ function disableIsaacScriptWatcherAutomaticRestart(watcherModPath) {
27
+ const mainLuaPath = path.join(watcherModPath, MAIN_LUA);
28
+ const mainLua = readFile(mainLuaPath);
29
+ const modifiedMainLua = mainLua.replace("local RESTART_GAME_ON_RECOMPILATION = true", "local RESTART_GAME_ON_RECOMPILATION = false");
30
+ writeFile(mainLuaPath, modifiedMainLua);
31
+ }
@@ -0,0 +1,44 @@
1
+ import syncDirectory from "@zamiell/sync-directory";
2
+ import { getArgs } from "isaacscript-common-node";
3
+ import { assertDefined } from "isaacscript-common-ts";
4
+ import { FILE_SYNCED_MESSAGE } from "../../../constants.js";
5
+ const SUBPROCESS_NAME = "directory syncer";
6
+ let modSourcePath;
7
+ let modTargetPath;
8
+ init();
9
+ function init() {
10
+ const args = getArgs();
11
+ const firstArg = args[0];
12
+ assertDefined(firstArg, `The ${SUBPROCESS_NAME} process did not get a valid first argument.`);
13
+ const secondArg = args[1];
14
+ assertDefined(secondArg, `The ${SUBPROCESS_NAME} process did not get a valid second argument.`);
15
+ modSourcePath = firstArg;
16
+ modTargetPath = secondArg;
17
+ syncDirectory(modSourcePath, modTargetPath, {
18
+ watch: true,
19
+ type: "copy",
20
+ afterEachSync,
21
+ onError,
22
+ chokidarWatchOptions: {
23
+ awaitWriteFinish: {
24
+ stabilityThreshold: 1000,
25
+ },
26
+ },
27
+ });
28
+ }
29
+ function afterEachSync(params) {
30
+ if (params === undefined) {
31
+ return;
32
+ }
33
+ if (params.eventType !== "init:copy") {
34
+ send(`${FILE_SYNCED_MESSAGE} ${params.relativePath}`);
35
+ }
36
+ }
37
+ function onError(err) {
38
+ send(`The directory syncer encountered an error: ${err.message}`);
39
+ }
40
+ function send(msg) {
41
+ if (process.send !== undefined) {
42
+ process.send(msg);
43
+ }
44
+ }