diff --git a/Test/src/Main/js/Test.js b/Test/src/Main/js/Test/Main.js similarity index 100% rename from Test/src/Main/js/Test.js rename to Test/src/Main/js/Test/Main.js diff --git a/Test/src/Main/js/Test/Main2.js b/Test/src/Main/js/Test/Main2.js new file mode 100644 index 0000000..0fa165a --- /dev/null +++ b/Test/src/Main/js/Test/Main2.js @@ -0,0 +1 @@ +module.exports = "a" \ No newline at end of file diff --git a/Test/src/Main/package.info.json b/Test/src/Main/package.info.json index b9def21..ac02a22 100644 --- a/Test/src/Main/package.info.json +++ b/Test/src/Main/package.info.json @@ -1,25 +1,37 @@ { - "Id": "TypeWriterTest", - "Name": "TypeWriter Test", - "Description": "A Test for TypeWriter", - "Version": "1.0.0", - "Authors": { - "Developers": [ - "CoreByte" - ], - "Contributors": [] - }, - "Contact": { - "Website": "", - "Source": "", - "Socials": {} - }, - "Dependencies": [ - "npm+node-fetch@3.3.1", - "npm+types/node-fetch@2.6.4", - "npm+discord.js@14.12.1" - ], - "Entrypoints": { - "Main": "Test" - } + "Id": "TypeWriterTest", + "Name": "TypeWriter Test", + "Description": "A Test for TypeWriter", + "Version": "1.0.0", + "Authors": { + "Developers": [ + "CoreByte" + ], + "Contributors": [] + }, + "Contact": { + "Website": "", + "Source": "", + "Socials": {} + }, + "Dependencies": [ + "npm+discord.js@14.14.1", + "npm+argparse@2.0.1", + "npm+colors@1.4.0", + "npm+extract-zip@2.0.1", + "npm+fs-extra@11.1.1", + "npm+get-caller-file@2.0.5", + "npm+is-builtin-module@3.2.1", + "npm+is-online@10.0.0", + "npm+klaw-sync@6.0.0", + "npm+node-fetch@3.3.2", + "npm+randomstring@1.3.0", + "npm+require-from-string@2.0.2", + "npm+require-main-filename@2.0.0", + "npm+sync-fetch@0.5.2", + "npm+tar@6.2.0" + ], + "Entrypoints": { + "Main": "Test" + } } diff --git a/TypeWriter/Actions/build/Index.js b/TypeWriter/Actions/build/Index.js index 802d9cd..ddacdf6 100644 --- a/TypeWriter/Actions/build/Index.js +++ b/TypeWriter/Actions/build/Index.js @@ -1,11 +1,15 @@ -const BuildHelper = require("../../Lib/BuildHelper.js") +const BuilderClass = require("../../Classes/Builder.js") module.exports.Name = "Build" module.exports.Execute = async function() { const InputPath = TypeWriter.Arguments.input const InputBranch = TypeWriter.Arguments.branch + const OutputPath = TypeWriter.Arguments.output - const BuildId = BuildHelper.Build(InputPath, InputBranch) - BuildHelper.CompressBuild(BuildId, TypeWriter.Arguments.output) - BuildHelper.CleanupBuild(BuildId) + const Builder = new BuilderClass(InputPath, InputBranch, OutputPath) + await Builder.ValidatePackageInfo() + await Builder.ScanCode() + await Builder.ScanResources() + await Builder.Compress() + await Builder.Cleanup() } \ No newline at end of file diff --git a/TypeWriter/Actions/execute/Index.js b/TypeWriter/Actions/execute/Index.js index 055243a..1d7c55c 100644 --- a/TypeWriter/Actions/execute/Index.js +++ b/TypeWriter/Actions/execute/Index.js @@ -1,21 +1,21 @@ const FS = require("fs-extra") -const RandomString = require("randomstring") const Path = require("path") const LoadEnvoirment = require("../../Lib/LoadEnvoirment") +const RandomString = require("../../Lib/RandomString.js") + module.exports.Name = "Execute" module.exports.Execute = async function() { const InputPath = TypeWriter.Arguments.input - const ExecuteId = RandomString.generate(32) + const ExecuteId = RandomString(32) const ExecuteFolder = Path.normalize(`${TypeWriter.Folder}/Cache/ExecuteCache/${ExecuteId}/`) + FS.mkdirSync(ExecuteFolder) TypeWriter.Logger.Debug(`Input is ${InputPath}`) TypeWriter.Logger.Debug(`ExecuteId is ${ExecuteId}`) TypeWriter.Logger.Debug(`ExecuteFolder is ${ExecuteFolder}`) - FS.mkdirSync(ExecuteFolder) - await LoadEnvoirment(ExecuteFolder) - const Package = TypeWriter.LoadFile(InputPath) - TypeWriter.LoadEntrypointAsync(Package.Id, "Main") + const Package = await TypeWriter.PackageManager.LoadPackage(OutputFile) + await Package.LoadEntrypoint("Main") } \ No newline at end of file diff --git a/TypeWriter/Actions/run/Index.js b/TypeWriter/Actions/run/Index.js index ee32855..31fcc77 100644 --- a/TypeWriter/Actions/run/Index.js +++ b/TypeWriter/Actions/run/Index.js @@ -1,30 +1,30 @@ -const RandomString = require("randomstring") const FS = require("fs-extra") const Path = require("path") -const BuildHelper = require("../../Lib/BuildHelper.js") +const BuilderClass = require("../../Classes/Builder.js") const LoadEnvoirment = require("../../Lib/LoadEnvoirment") +const RandomString = require("../../Lib/RandomString.js") module.exports.Name = "Run" module.exports.Execute = async function() { const InputPath = TypeWriter.Arguments.input const InputBranch = TypeWriter.Arguments.branch - const BuildId = BuildHelper.Build(InputPath, InputBranch) - const ExecuteId = RandomString.generate(32) + const ExecuteId = RandomString(32) + const ExecuteFolder = Path.normalize(`${TypeWriter.Folders.Cache.ExecuteCacheFolder}/${ExecuteId}/`) + FS.mkdirSync(ExecuteFolder) + TypeWriter.Logger.Debug(`ExecuteId is ${ExecuteId}`) - const ExecuteFolder = Path.normalize(`${TypeWriter.Folder}/Cache/ExecuteCache/${ExecuteId}/`) TypeWriter.Logger.Debug(`ExecuteFolder is ${ExecuteFolder}`) - - FS.mkdirSync(ExecuteFolder) - BuildHelper.CompressBuild(BuildId, ExecuteFolder) - BuildHelper.CleanupBuild(BuildId) - if (BuildId == false) { - return - } + const Builder = new BuilderClass(InputPath, InputBranch, ExecuteFolder) + await Builder.ValidatePackageInfo() + await Builder.ScanCode() + await Builder.ScanResources() + const OutputFile = await Builder.Compress() + await Builder.Cleanup() await LoadEnvoirment(ExecuteFolder) - const Package = TypeWriter.LoadFile(`${ExecuteFolder}/${FS.readdirSync(ExecuteFolder)}`) - TypeWriter.LoadEntrypointAsync(Package.Id, "Main") + const Package = await TypeWriter.PackageManager.LoadPackage(OutputFile) + await Package.LoadEntrypoint("Main") } \ No newline at end of file diff --git a/TypeWriter/Assets/package.info.json b/TypeWriter/Assets/package.info.json index c6865ee..b0969cf 100644 --- a/TypeWriter/Assets/package.info.json +++ b/TypeWriter/Assets/package.info.json @@ -8,9 +8,7 @@ "Developers": [ "CoreByte" ], - "Contributors": [ - - ] + "Contributors": [] }, "Contact": { @@ -19,14 +17,9 @@ "Socials": {} }, - "Dependencies": [ - { - "Source": "LIT", - "Package": "coro-spawn", - "Version": null, - "Type": "Packaged" - } - ], + "Dependencies": [], + + "Preload": [], "Entrypoints": { "Main": "me.corebyte.test" diff --git a/TypeWriter/Classes/Builder.js b/TypeWriter/Classes/Builder.js new file mode 100644 index 0000000..d6790ce --- /dev/null +++ b/TypeWriter/Classes/Builder.js @@ -0,0 +1,173 @@ +const FS = require("fs-extra") +const Path = require("path") +const KlawSync = require('klaw-sync') +const RandomString = require("../Lib/RandomString.js") +const DependencyParser = require("../Lib/DependencyParser.js") +const Tar = require("tar") + +async function FixDependencyVersions(Dependencies) { + let Updated = false + const VersionFixes = [] + + let DependencyIndex = -1 + for (const Dependency of Dependencies) { + let ParsedDependency = DependencyParser.Parse(Dependency) + DependencyIndex++ + + if (ParsedDependency.Version != undefined) { continue } + + Updated = true + TypeWriter.Logger.Warning(`No version specified for ${ParsedDependency.FullName}, fixing...`) + VersionFixes.push( + [ + DependencyIndex, + ParsedDependency, + async function (DependencyIndex, ParsedDependency) { + const LatestVersion = await TypeWriter.DependencyManager.GetLatestVersion(ParsedDependency) + ParsedDependency.Version = LatestVersion + Dependencies[DependencyIndex] = DependencyParser.Format(ParsedDependency) + } + ] + + ) + + } + + await Promise.all(VersionFixes.map(D => D[2](D[0], D[1]))) + + return [Updated, Dependencies] +} + +async function ScanCode(ScanFolder, Extension) { + ScanFolder = Path.normalize(ScanFolder) + const ScannedCode = {} + const Files = KlawSync(ScanFolder, { nodir: true }) + for (const File of Files) { + const FilePath = File.path + const CodePath = FilePath.split(ScanFolder)[1].replaceAll("\\", "/").replaceAll("/", ".").substring(1).split(".").slice(0, -1).join(".") + + ScannedCode[CodePath] = { + Type: Extension, + Code: encodeURIComponent(FS.readFileSync(FilePath, "utf-8")) + } + + if (CodePath.endsWith(".Main")) { + ScannedCode[CodePath.substring(0, CodePath.length - 5)] = { + Type: "Redirect", + Path: CodePath + } + } else if (CodePath.endsWith(".Index")) { + ScannedCode[CodePath.substring(0, CodePath.length - 6)] = { + Type: "Redirect", + Path: CodePath + } + } + } + + return ScannedCode +} + +class Builder { + constructor(Folder, Branch, OutputPath) { + this.BuildFolder = Folder + this.BranchFolder = Path.normalize(`${Folder}/${Branch}/`) + this.OutputPath = OutputPath + this.BuildId = RandomString(32) + this.BuildFolder = Path.normalize(`${TypeWriter.Folders.Cache.BuildCacheFolder}/${this.BuildId}/`) + this.PackageInfoFile = Path.normalize(`${this.BranchFolder}/package.info.json`) + + TypeWriter.Logger.Debug(`Building to ${this.BuildFolder}`) + + FS.ensureDirSync(this.BuildFolder) + FS.cpSync(this.BranchFolder + "/package.info.json", this.BuildFolder + "/package.info.json") + + this.CreateRequiredFolders() + } + + CreateRequiredFolders() { + FS.ensureDirSync(this.BranchFolder + "/js") + FS.ensureDirSync(this.BranchFolder + "/lua") + FS.ensureDirSync(this.BranchFolder + "/resources") + } + + async ValidatePackageInfo() { + const PackageInfo = FS.readJsonSync(this.PackageInfoFile) + let NeedsUpdate = false + + const [Updated, Dependencies] = await FixDependencyVersions(PackageInfo.Dependencies) + NeedsUpdate = Updated || NeedsUpdate + PackageInfo.Dependencies = Dependencies + + if (NeedsUpdate) { + FS.writeJsonSync(this.PackageInfoFile, PackageInfo, { spaces: 4 }) + FS.writeJSONSync(this.BuildFolder + "/package.info.json", PackageInfo, { spaces: 4 }) + } + } + + async ScanCode() { + const ScannedCode = Object.assign( + {}, + await ScanCode(this.BranchFolder + "/lua", "lua"), + await ScanCode(this.BranchFolder + "/js", "js") + ) + + FS.writeJSONSync( + `${this.BuildFolder}/Code.json`, + ScannedCode, + { + spaces: "\t" + } + ) + } + + async ScanResources() { + const ResourceFolder = Path.normalize(`${this.BranchFolder}/resources`) + const DestinationResourceFolder = `${this.BuildFolder}/resources/` + + const ResourceIndex = KlawSync( + this.BranchFolder + "/resources", + { + nodir: true + } + ).map( + (File) => { + const FilePath = File.path + const ResourceFilePath = Path.normalize(FilePath.split(ResourceFolder)[1]).replaceAll("\\", "/") + return ResourceFilePath + } + ) + + FS.copySync(ResourceFolder, DestinationResourceFolder) + FS.writeJSONSync( + `${this.BuildFolder}/ResourceIndex.json`, + ResourceIndex, + { + spaces: "\t" + } + ) + } + + async Compress() { + const PackageInfo = FS.readJsonSync(this.PackageInfoFile) + const OutputFile = Path.join(this.OutputPath + `/${PackageInfo.Id}.twr`) + TypeWriter.Logger.Debug(`Outputting to ${OutputFile} in ${this.BuildFolder}`) + Tar.create( + { + file: OutputFile, + cwd: this.BuildFolder, + sync: true, + noMtime: true, + portable: true + }, + FS.readdirSync(this.BuildFolder) + ) + + return OutputFile + } + + async Cleanup() { + FS.rmSync(this.BuildFolder, { recursive: true, force: true }) + } +} + +module.exports = Builder \ No newline at end of file diff --git a/TypeWriter/Classes/DependencyManager/Index.js b/TypeWriter/Classes/DependencyManager/Index.js new file mode 100644 index 0000000..aa26b5e --- /dev/null +++ b/TypeWriter/Classes/DependencyManager/Index.js @@ -0,0 +1,45 @@ +class DependencyManager { + constructor() { + this.Managers = { + NPM: new (require("./Managers/NPM.js")), + // LIT: new (require("./Managers/LIT.js")), + } + } + + GetManager(Manager) { + return this.Managers[Manager.toUpperCase()] + } + + async GetLatestVersion(Depencency) { + const Manager = this.GetManager(Depencency.Source) + return await Manager.GetLatestVersion(Depencency) + } + + async GetDependencyFolder(Depencency, Version) { + const Manager = this.GetManager(Depencency.Source) + return await Manager.GetDependencyFolder(Depencency, Version) + } + + async Exists(Depencency) { + const Manager = this.GetManager(Depencency.Source) + return await Manager.Exists(Depencency) + } + + async AddDependencyToQueue(Depencency) { + const Manager = this.GetManager(Depencency.Source) + return await Manager.AddDependencyToQueue(Depencency) + } + + async ExecuteQueue() { + const Promises = [] + for (const ManagerName in this.Managers) { + const Manager = this.Managers[ManagerName] + Promises.push(Manager.ExecuteQueue()) + } + return await Promise.all(Promises) + } + + +} + +module.exports = DependencyManager \ No newline at end of file diff --git a/TypeWriter/Classes/DependencyManager/Managers/NPM.js b/TypeWriter/Classes/DependencyManager/Managers/NPM.js new file mode 100644 index 0000000..31fb3bb --- /dev/null +++ b/TypeWriter/Classes/DependencyManager/Managers/NPM.js @@ -0,0 +1,197 @@ +const FetchJson = require("../../../Lib/FetchJson.js") +const Fetch = require("node-fetch") +const FS = require("fs-extra") +const Path = require("path") +const Pall = require("p-all") +const FetchFile = require("../../../Lib/FetchFile.js") +const Tar = require("tar") +const FSHelpers = require("../../../Lib/FSHelpers") + +async function SplitNameAndVersion(NameAndVersion) { + const SplitDependency = NameAndVersion.split("@") + let DependencyName = SplitDependency[0] + let DependencyVerison = SplitDependency[1] + if (SplitDependency.length == 3) { + DependencyName = SplitDependency[0] + "@" + SplitDependency[1] + DependencyVerison = SplitDependency[2] + } + return [DependencyName, DependencyVerison] +} + +async function GetDependencyDownloadLink(Name, Version) { + let AuthorLessName = Name + if (Name.startsWith("@")) { + AuthorLessName = Name.split("/")[1] + } + return `https://registry.npmjs.org/${Name}/-/${AuthorLessName}-${Version}.tgz` +} + +async function BuildDependencyTree(Dependencies) { + const Promises = [] + for (const DependencyName in Dependencies) { + const DependencyVersion = Dependencies[DependencyName] + Promises.push( + async function() { + const [_, DependencyData] = await FetchJson(`https://cdn.jsdelivr.net/npm/${DependencyName}@${DependencyVersion}/package.json`) + return [DependencyData.name, DependencyData.version, await BuildDependencyTree(DependencyData.dependencies)] + } + ) + } + const DependencyData = await Promise.all(Promises.map(Promise => Promise())) + + const ReturnData = {} + for (const Dependency of DependencyData) { + ReturnData[Dependency[0] + "@" + Dependency[1]] = Dependency[2] + } + return ReturnData +} + +async function FlatDependencyTree(DependencyTree, Dependencies = []) { + for (const Dependency in DependencyTree) { + const ChildDependencies = DependencyTree[Dependency] + if (Dependencies.includes(Dependency)) { continue } + Dependencies.push(Dependency) + await FlatDependencyTree(ChildDependencies, Dependencies) + } + return Dependencies +} + +async function LinkDependencies(DependencyTree) { + for (const Dependency in DependencyTree) { + const SubDependencies = DependencyTree[Dependency] + const DependencyFolder = `${TypeWriter.Folders.Cache.ModuleCache.NPMFolder}/${Dependency}/` + const NodeModulesFolder = `${DependencyFolder}/node_modules/` + FS.ensureDirSync(NodeModulesFolder) + for (const SubDependency in SubDependencies) { + const [SubDependencyName] = await SplitNameAndVersion(SubDependency) + const SubDependencyFolder = `${TypeWriter.Folders.Cache.ModuleCache.NPMFolder}/${SubDependency}/` + if (SubDependency.startsWith("@")) { + const SplitDependency = SubDependency.split("/") + const SubDependencyFolder = `${NodeModulesFolder}/${SplitDependency[0]}/` + FS.ensureDirSync(SubDependencyFolder) + } + const SymlinkLocation = `${NodeModulesFolder}/${SubDependencyName}` + if (FS.existsSync(SymlinkLocation)) { continue } + FS.symlinkSync( + SubDependencyFolder, + `${NodeModulesFolder}/${SubDependencyName}`, + TypeWriter.OS == "win32" ? 'junction' : 'dir' + ) + } + await LinkDependencies(SubDependencies) + } +} + +async function GetDependency(Name, Version) { + const FullDependencyName = `${Name}@${Version}` + + const NPMFolder = `${TypeWriter.Folders.Cache.ModuleCache.NPMFolder}/` + const DependencyFolder = `${NPMFolder}/${FullDependencyName}/` + const UnpackFolder = `${NPMFolder}/${FullDependencyName}_Unpack/` + const TarFile = `${NPMFolder}/${FullDependencyName}.tar.gz` + + const DownloadLink = await GetDependencyDownloadLink(Name, Version) + await FetchFile(DownloadLink, TarFile) + + if (FS.existsSync(UnpackFolder)) { return } + FS.ensureDirSync(UnpackFolder) + await Tar.extract( + { + file: TarFile, + cwd: UnpackFolder, + preserveOwner: false + } + ) + + const MoveFolder = FSHelpers.FindDown(UnpackFolder, "package.json") + while (true) { + try { + await FS.moveSync(MoveFolder, DependencyFolder) + break + } catch (error) { + TypeWriter.Logger.Warning(`Failed to move ${error}`) + } + } +} + +class NPM { + constructor() { + this.DependencyQueue = [] + } + + async GetLatestVersion(Dependency) { + const [_, Data] = await FetchJson(`https://cdn.jsdelivr.net/npm/${Dependency.AtFullName}/package.json`) + return Data.version + } + + async GetDependencyFolder(Dependency, Version) { + if (Version) { + return `${TypeWriter.Folders.Cache.ModuleCache.NPMFolder}/${Dependency}@${Version}/` + } else { + return `${TypeWriter.Folders.Cache.ModuleCache.NPMFolder}/${Dependency.AtFullName}@${Dependency.Version}/` + } + } + + async Exists(Dependency, Version) { + if (Version) { + if (Version == true) { + Version = Dependency.Version + Dependency = Dependency.AtFullName + } + const DependencyFolder = await this.GetDependencyFolder(Dependency, Version) + const FolderExists = FS.existsSync(DependencyFolder) + return FolderExists + } else { + const DependencyFolder = await this.GetDependencyFolder(Dependency) + const FolderExists = FS.existsSync(DependencyFolder) + if (!FolderExists) { + const [Response] = await FetchJson(`https://cdn.jsdelivr.net/npm/${Dependency.AtFullName}/package.json`) + const Exists = Response.status == 200 + return Exists + } + return true + } + } + + async AddDependencyToQueue(Dependency) { + if (await this.Exists(Dependency, true)) { + TypeWriter.Logger.Debug(`Dependency ${Dependency.AtFullName} already downloaded, skipping...`) + return + } + this.DependencyQueue.push(Dependency) + } + + async ExecuteQueue() { + if (this.DependencyQueue.length == 0) { return } + TypeWriter.Logger.Information(`Downloading ${this.DependencyQueue.length} NPM dependencies...`) + const MappedDependencies = {} + for (const Dependency of this.DependencyQueue) { + MappedDependencies[Dependency.AtFullName] = Dependency.Version + } + TypeWriter.Logger.Information(`Building dependency tree...`) + const DependencyTree = await BuildDependencyTree(MappedDependencies) + const FlattendDependencyTree = await FlatDependencyTree(DependencyTree) + + TypeWriter.Logger.Information(`Downloading ${FlattendDependencyTree.length} NPM dependencies...`) + const DependencyPromises = [] + for (const Dependency of FlattendDependencyTree) { + const [DependencyName, DependencyVerison] = await SplitNameAndVersion(Dependency) + + DependencyPromises.push( + async function() { + await GetDependency(DependencyName, DependencyVerison) + TypeWriter.Logger.Information(`Downloaded ${DependencyName}@${DependencyVerison}`) + } + ) + } + await Promise.all(DependencyPromises.map(Promise => Promise())) + + TypeWriter.Logger.Information(`Linking dependencies...`) + await LinkDependencies(DependencyTree) + TypeWriter.Logger.Information(`Done!`) + + } + +} + +module.exports = NPM \ No newline at end of file diff --git a/TypeWriter/Classes/Logger.js b/TypeWriter/Classes/Logger.js new file mode 100644 index 0000000..8785a10 --- /dev/null +++ b/TypeWriter/Classes/Logger.js @@ -0,0 +1,61 @@ +const Colors = require("colors/safe") + +function Pad(num, size) { + var s = "00" + num; + return s.substring(s.length-size); +} + +class Logger { + constructor(Name, LogLevel=3) { + this.Name = Name + this.LogLevel = LogLevel + + this.LogLevels = [ + Colors.underline(Colors.red("FATAL")), + Colors.red("ERROR"), + Colors.yellow("WARN "), + Colors.brightGreen("INFO "), + Colors.cyan("DEBUG") + ] + } + + Log(Level, Message) { + if (Level > this.LogLevel) { return } + + const Time = new Date() + const TimeString = `${Time.getFullYear()}-${Pad(Time.getMonth() + 1, 2)}-${Pad(Time.getDate(), 2)} ${Pad(Time.getHours(), 2)}:${Pad(Time.getMinutes(), 2)}:${Pad(Time.getSeconds(), 2)}` + console.log( + `[ ${Colors.bold(this.LogLevels[Level])} ] [ ${TimeString} ] [ ${this.Name} ] : ${Message}` + ) + } + + Debug(Message) { + this.Log(4, Message) + } + + Info(Message) { + this.Log(3, Message) + } + + Information(Message) { + this.Info(Message) + } + + Warn(Message) { + this.Log(2, Message) + } + + Warning(Message) { + this.Warn(Message) + } + + Error(Message) { + this.Log(1, Message) + } + + Fatal(Message) { + this.Log(0, Message) + } +} + +module.exports = Logger \ No newline at end of file diff --git a/TypeWriter/Classes/PackageManager.js b/TypeWriter/Classes/PackageManager.js new file mode 100644 index 0000000..351521f --- /dev/null +++ b/TypeWriter/Classes/PackageManager.js @@ -0,0 +1,136 @@ +const SingleTar = require("../Lib/SingleTar") +const DependencyParser = require("../Lib/DependencyParser") +const FS = require("fs-extra") + +function SingleJson(FilePath, Path) { + return JSON.parse(SingleTar(FilePath, Path)) +} + +class Package { + constructor(FilePath, IsSubPackage = false) { + this.PackageInfo = SingleJson(FilePath, "package.info.json") + this.Code = SingleJson(FilePath, "Code.json") + this.ResourceIndex = SingleJson(FilePath, "ResourceIndex.json") + + this.FilePath = FilePath + this.IsSubPackage = IsSubPackage + this.SubPackages = [] + this.ExecuteFolder = `${TypeWriter.ExecuteFolder}/${this.PackageInfo.Id}/` + FS.ensureDirSync(this.ExecuteFolder) + this.NodeModulesFolder = `${this.ExecuteFolder}/node_modules/` + FS.ensureDirSync(this.NodeModulesFolder) + } + + async FetchDependencies() { + const DependencyObjects = this.ListDependencyObjects() + for (const Dependency of DependencyObjects) { + await TypeWriter.DependencyManager.AddDependencyToQueue(Dependency) + } + + for (const SubPackagePath of this.PackageInfo.Preload || []) { + this.SubPackages.push(await TypeWriter.PackageManager.LoadPackage(TypeWriter.ResourceManager.GetFilePath(SubPackagePath), true)) + } + + if (!this.IsSubPackage) { + await TypeWriter.DependencyManager.ExecuteQueue() + await this.LinkDependencies() + + for (const SubPackage of this.SubPackages) { + await SubPackage.LinkDependencies() + } + } + } + + async LinkDependencies() { + const DependencyObjects = this.ListDependencyObjects() + for (const Dependency of DependencyObjects) { + const DependencyFolder = await TypeWriter.DependencyManager.GetDependencyFolder(Dependency) + const ModulesDependencyFolder = `${this.NodeModulesFolder}/${Dependency.AtFullName}/` + FS.symlinkSync( + DependencyFolder, + ModulesDependencyFolder, + TypeWriter.OS == "win32" ? 'junction' : 'dir' + ) + } + } + + GetPackageInfo() { + return this.PackageInfo + } + + GetEntrypoints() { + return this.PackageInfo.Entrypoints + } + + ListDependencyObjects() { + return this.PackageInfo.Dependencies.map(Dependency => DependencyParser.Parse(Dependency)) + } + + async Import(ImportPath) { + const ImportData = this.Code[ImportPath] + if (!ImportData) { + const NotFoundError = new Error(`Import '${ImportPath}' not found`) + NotFoundError.code = "TYPEWRITER_IMPORT_NOT_FOUND" + throw NotFoundError + } + + if (ImportData.Type === "Redirect") { + return await this.Import(ImportData.Path) + } + + const CodeData = decodeURIComponent(ImportData.Code) + if (ImportData.Type === "lua") { + console.log("Importing Lua") + } else if (ImportData.Type === "js") { + return await TypeWriter.JavaScript.LoadStringWrapped(CodeData, `${this.PackageInfo.Id}: ${ImportPath}`) + } + } + + async LoadEntrypoint(Entrypoint) { + return await this.Import(this.GetEntrypoints()[Entrypoint]) + } +} + +class PackageManager { + constructor() { + this.LoadedPackages = {} + } + + async LoadPackage(FilePath, IsSubPackage = false) { + const LoadedPackage = new Package(FilePath, IsSubPackage) + this.LoadedPackages[LoadedPackage.PackageInfo.Id] = LoadedPackage + await LoadedPackage.FetchDependencies() + return LoadedPackage + } + + ListPackageIds() { + return Object.keys(this.LoadedPackages) + } + + GetPackage(Id) { + return this.LoadedPackages[Id] + } + + IsPackageLoaded(Id) { + return !!this.LoadedPackages[Id] + } + + async Import(ImportPath) { + for (const Package of Object.values(this.LoadedPackages)) { + try { + return await Package.Import(ImportPath) + } catch (error) { + if (error.code != "TYPEWRITER_IMPORT_NOT_FOUND") { + throw error + } + } + } + + const NotFoundError = new Error(`Import '${ImportPath}' not found`) + NotFoundError.code = "TYPEWRITER_IMPORT_NOT_FOUND" + throw NotFoundError + } + +} + +module.exports = PackageManager \ No newline at end of file diff --git a/TypeWriter/Classes/ResourceManager.js b/TypeWriter/Classes/ResourceManager.js new file mode 100644 index 0000000..3579764 --- /dev/null +++ b/TypeWriter/Classes/ResourceManager.js @@ -0,0 +1,50 @@ +const SingleTar = require("../Lib/SingleTar") +const FS = require("fs-extra") +const Path = require("path") + +function ParseResourcePath(Id, ResourcePath) { + if (ResourcePath != undefined) { + return [Id, ResourcePath] + } + const Split = Id.split(":") + return [Split[0], Split[1]] +} + +class ResourceManager { + constructor() { + + } + + ResourceExists(Id, ResourcePath) { + [Id, ResourcePath] = ParseResourcePath(Id, ResourcePath) + const Package = TypeWriter.PackageManager.GetPackage(Id) + return Package.ResourceIndex.includes(ResourcePath) + } + + ListResources(Id) { + const Package = TypeWriter.PackageManager.GetPackage(Id) + return Package.ResourceIndex + } + + GetRaw(Id, ResourcePath) { + [Id, ResourcePath] = ParseResourcePath(Id, ResourcePath) + const Package = TypeWriter.PackageManager.GetPackage(Id) + return SingleTar(Package.FilePath, `resources${ResourcePath}`) + } + + GetJson(Id, ResourcePath) { + return JSON.parse(this.GetRaw(Id, ResourcePath)) + } + + GetFilePath(Id, ResourcePath) { + [Id, ResourcePath] = ParseResourcePath(Id, ResourcePath) + const Package = TypeWriter.PackageManager.GetPackage(Id) + const OutputPath = `${Package.ExecuteFolder}/resources${ResourcePath}` + FS.ensureDirSync(Path.dirname(OutputPath)) + FS.writeFileSync(OutputPath, this.GetRaw(Id, ResourcePath), "binary") + return OutputPath + } + +} + +module.exports = ResourceManager \ No newline at end of file diff --git a/TypeWriter/Index.js b/TypeWriter/Index.js index 9d088c3..c65b064 100644 --- a/TypeWriter/Index.js +++ b/TypeWriter/Index.js @@ -2,57 +2,93 @@ const FS = require("fs-extra") const FSHelpers = require("./Lib/FSHelpers") const Path = require("path") -const ArgumentData = require("./Registry/Arguments") -const Module = require("module") - -global.TypeWriter = {} -//Arguments -TypeWriter.Arguments = ArgumentData.Arguments -TypeWriter.ApplicationArguments = ArgumentData.Unknown -//Os -TypeWriter.OS = process.platform -//File Paths -TypeWriter.Folder = FSHelpers.FindUp(Path.resolve(process.argv0, "../"), "InstallationDirectory", 40) || Path.resolve(`${process.argv0}/../`) -TypeWriter.Executable = process.execPath -TypeWriter.ApplicationData = `${TypeWriter.Folder}/ApplicationData/` -//Other -TypeWriter.Logger = require("./Lib/Logger") -TypeWriter.OriginalRequire = Module.prototype.require -TypeWriter.PackageManagers = require("./Lib/PackageManagers/Index.js") - -async function Main() { - //Check if valid install - const Installer = require("./Installer/Index.js") - if (FS.existsSync(`${TypeWriter.Folder}/InstallationDirectory`)) { - TypeWriter.Logger.Debug("Valid installation found, Trying postinstall scripts") - await Installer.PostInstall() - } else { - TypeWriter.Logger.Information("Invalid install found") - await Installer.Install() - process.exit(0) +const Logger = require("./Classes/Logger.js") +const DepencencyManager = require("./Classes/DependencyManager/Index.js") +const PackageManager = require("./Classes/PackageManager.js") +const ResourceManager = require("./Classes/ResourceManager.js") + +class TypeWriter { + constructor() { + this.ArgumentData = require("./Registry/Arguments") + this.Arguments = this.ArgumentData.Arguments + this.ApplicationArguments = this.ArgumentData.Unknown + + this.OS = process.platform + this.OriginalRequire = require("module").prototype.require + + this.InstallationFolder = global.TypeWriterAltInstallationFolder || FSHelpers.FindUp(Path.resolve(process.argv0, "../"), "InstallationDirectory", 40) || Path.resolve(`${process.argv0}/../`) + this.ApplicationData = Path.normalize(`${this.InstallationFolder}/ApplicationData/`) + this.Executable = process.execPath + + this.Logger = this.CreateLogger("TypeWriter", process.env.TypeWriterLogLevel || 3) + this.DependencyManager = new DepencencyManager() + this.PackageManager = new PackageManager() + this.ResourceManager = new ResourceManager() + + this.Actions = require("./Actions/List.js") + } + + CreateLogger(LoggerName) { + return new Logger(LoggerName) } + + RunAction(ActionName) { + if (!ActionName && !this.Arguments.action) { + return false + } else { + ActionName = this.Arguments.action + } + + const Action = this.Actions[ActionName] + if (!Action) { + this.Logger.Error(`Action '${ActionName}' not found`) + process.exitCode = 1 + return false + } - //Create folders in Exe folder - FS.ensureDirSync(TypeWriter.ApplicationData) - FS.ensureDirSync(`${TypeWriter.Folder}/Binaries/`) - FS.ensureDirSync(`${TypeWriter.Folder}/Cache/`) - FS.ensureDirSync(`${TypeWriter.Folder}/Cache/BuildCache/`) - FS.ensureDirSync(`${TypeWriter.Folder}/Cache/ExecuteCache/`) - FS.ensureDirSync(`${TypeWriter.Folder}/Cache/ModuleCache/`) - FS.ensureDirSync(`${TypeWriter.Folder}/Cache/ModuleCache/NPM/`) - FS.ensureDirSync(`${TypeWriter.Folder}/Cache/ModuleCache/NPM/Modules/`) - FS.ensureDirSync(`${TypeWriter.Folder}/Cache/ModuleCache/NPM/ModuleTars/`) - FS.ensureDirSync(`${TypeWriter.Folder}/Cache/ModuleCache/NPM/Unpack/`) - FS.ensureDirSync(`${TypeWriter.Folder}/Cache/ModuleCache/LIT/`) - - //Run action - if (TypeWriter.Arguments.action) { - const Actions = require("./Actions/List.js") - const Action = Actions[TypeWriter.Arguments.action] - await Action.Execute() - } else { - ArgumentData.Parser.print_help() + Action.Execute() + return true + } + + ShowHelp() { + console.log(this.ArgumentData.Parser.format_help()) + } + + CreateFolders() { + this.Folders = { + BinariesFolder: `${this.InstallationFolder}/Binaries/`, + CacheFolder: `${this.InstallationFolder}/Cache/`, + Cache: { + BuildCacheFolder: `${this.InstallationFolder}/Cache/BuildCache/`, + ExecuteCacheFolder: `${this.InstallationFolder}/Cache/ExecuteCache/`, + ModuleCacheFolder: `${this.InstallationFolder}/Cache/ModuleCache/`, + ModuleCache: { + NPMFolder: `${this.InstallationFolder}/Cache/ModuleCache/NPM/`, + LITFolder: `${this.InstallationFolder}/Cache/ModuleCache/LIT/` + } + } + } + + FS.ensureDirSync(this.ApplicationData) + FS.ensureDirSync(this.Folders.BinariesFolder) + FS.ensureDirSync(this.Folders.CacheFolder) + FS.ensureDirSync(this.Folders.Cache.BuildCacheFolder) + FS.ensureDirSync(this.Folders.Cache.ExecuteCacheFolder) + FS.ensureDirSync(this.Folders.Cache.ModuleCacheFolder) + FS.ensureDirSync(this.Folders.Cache.ModuleCacheFolder) + FS.ensureDirSync(this.Folders.Cache.ModuleCache.NPMFolder) + FS.ensureDirSync(this.Folders.Cache.ModuleCache.LITFolder) } } -Main() \ No newline at end of file +process.title = "TypeWriter" +global.TypeWriter = new TypeWriter() + +require("./Installer/Index.js")() + +global.TypeWriter.CreateFolders() +module.exports = global.TypeWriter + +if (!global.TypeWriter.RunAction()) { + global.TypeWriter.ShowHelp() +} \ No newline at end of file diff --git a/TypeWriter/Installer/Install/AddToPath.js b/TypeWriter/Installer/Helpers/AddToPath.js similarity index 91% rename from TypeWriter/Installer/Install/AddToPath.js rename to TypeWriter/Installer/Helpers/AddToPath.js index 1186e22..f1965bb 100644 --- a/TypeWriter/Installer/Install/AddToPath.js +++ b/TypeWriter/Installer/Helpers/AddToPath.js @@ -41,7 +41,7 @@ function AddLineToFile(FilePath, AddLine) { FS.writeFileSync(FilePath, SplitLines.join("\n"), "utf-8") } -module.exports = async function (InstallLocation) { +module.exports = function (InstallLocation) { if (TypeWriter.OS != "win32") { RemoveLineFromFile(`${process.env.HOME}/.bash_profiles`, `alias TypeWriter="'${process.env.HOME}/Library/Application Support/TypeWriter//TypeWriter'"`) RemoveLineFromFile(`${process.env.HOME}/.bash_profiles`, `alias TypeWriter="'${InstallLocation}/TypeWriter'"`) @@ -57,5 +57,10 @@ module.exports = async function (InstallLocation) { `${process.env.LOCALAPPDATA}/Microsoft/WindowsApps/TypeWriter.bat`, `@echo off\n${process.env.APPDATA}/.TypeWriter/TypeWriter.exe %*` ) + + FS.writeFileSync( + `${process.env.LOCALAPPDATA}/Microsoft/WindowsApps/Twr.bat`, + `@echo off\n${process.env.APPDATA}/.TypeWriter/TypeWriter.exe %*` + ) } } \ No newline at end of file diff --git a/TypeWriter/Installer/Install/RemoveOldFiles.js b/TypeWriter/Installer/Helpers/RemoveOldFiles.js similarity index 95% rename from TypeWriter/Installer/Install/RemoveOldFiles.js rename to TypeWriter/Installer/Helpers/RemoveOldFiles.js index 3457837..3582876 100644 --- a/TypeWriter/Installer/Install/RemoveOldFiles.js +++ b/TypeWriter/Installer/Helpers/RemoveOldFiles.js @@ -6,7 +6,7 @@ function RemoveFileOrFolder(FolderPath) { return FS.removeSync(FolderPath) } -module.exports = async function(InstallLocation) { +module.exports = function(InstallLocation) { //Remove Old v5 files if (FS.existsSync(`${InstallLocation}/SessionStorage`)) { TypeWriter.Logger.Info("Found existing v5 installation, removing old files.") diff --git a/TypeWriter/Installer/Index.js b/TypeWriter/Installer/Index.js index b25785d..81491f0 100644 --- a/TypeWriter/Installer/Index.js +++ b/TypeWriter/Installer/Index.js @@ -1,27 +1,34 @@ const FS = require("fs-extra") +const Path = require("path") -module.exports = { - Install: async function () { - var InstallLocation - if (TypeWriter.OS == "win32") { - InstallLocation = `${process.env.APPDATA}/.TypeWriter/` - } else { - InstallLocation = `${process.env.HOME}/.TypeWriter/` - } +function InstallLocation() { + if (process.env.TYPEWRITER_INSTALL_DIR) { + return Path.resolve(process.env.TYPEWRITER_INSTALL_DIR) + } else if (TypeWriter.OS == "win32") { + return `${process.env.APPDATA}/.TypeWriter/` + } else { + return `${process.env.HOME}/.TypeWriter/` + } +} - FS.ensureDirSync(InstallLocation) +module.exports = function () { + if (FS.existsSync(`${TypeWriter.InstallationFolder}/InstallationDirectory`)) { return } - await (require("./Install/RemoveOldFiles"))(InstallLocation) + const InstallationDirectory = InstallLocation() - FS.mkdirSync(`${InstallLocation}/Cache`) - FS.writeFileSync(`${InstallLocation}/InstallationDirectory`, "") - const ExecutablePath = `${InstallLocation}/typewriter${TypeWriter.OS == "win32" ? ".exe" : ""}` - FS.copySync(process.argv0, ExecutablePath) + TypeWriter.Logger.Information(`Installing TypeWriter to '${InstallationDirectory}'`) - await (require("./Install/AddToPath"))(InstallLocation) - }, + FS.ensureDirSync(InstallationDirectory) - PostInstall: async function() { - // await (require("./PostInstall/NodeBinary"))() - } + require("./Helpers/RemoveOldFiles")(InstallationDirectory) + + FS.writeFileSync(`${InstallationDirectory}/InstallationDirectory`, "") + const ExecutablePath = `${InstallationDirectory}/typewriter${TypeWriter.OS == "win32" ? ".exe" : ""}` + FS.copySync(process.argv0, ExecutablePath) + + if (process.env.TYPEWRITER_INSTALL_NO_PATH) { process.exit(0) } + + require("./Helpers/AddToPath")(InstallationDirectory) + + process.exit(0) } \ No newline at end of file diff --git a/TypeWriter/Installer/Install/Index.js b/TypeWriter/Installer/Install/Index.js deleted file mode 100644 index 056933d..0000000 --- a/TypeWriter/Installer/Install/Index.js +++ /dev/null @@ -1,105 +0,0 @@ -const FS = require("fs-extra") -const Path = require("path") - -function InstallLocation() { - const InstallLocations = { - [true]: `${process.env.APPDATA}/.TypeWriter/`, - [false]: `${process.env.HOME}/.TypeWriter/` - } - - return InstallLocations[TypeWriter.OS == "win32"] -} - -function RemoveFileOrFolder(FolderPath) { - TypeWriter.Logger.Info(`Removing file ${Path.resolve(FolderPath)}`) - return FS.removeSync(FolderPath) -} - -function CreateDir(DirPath) { - if (FS.existsSync(DirPath)) { return } - FS.mkdirSync(DirPath) -} - -module.exports = async function () { - CreateDir(InstallLocation()) - - //Remove Old v5 files - if (FS.existsSync(`${InstallLocation()}/SessionStorage`)) { - TypeWriter.Logger.Info("Found existing v5 installation, removing old files.") - RemoveFileOrFolder(`${InstallLocation()}/Binary`) - RemoveFileOrFolder(`${InstallLocation()}/Config`) - RemoveFileOrFolder(`${InstallLocation()}/Internal`) - RemoveFileOrFolder(`${InstallLocation()}/PackageCache`) - RemoveFileOrFolder(`${InstallLocation()}/SessionStorage`) - RemoveFileOrFolder(`${InstallLocation()}/Temp`) - RemoveFileOrFolder(`${InstallLocation()}/TypeWriter`) - } - - if (FS.existsSync(`${InstallLocation()}/InstallationDirectory`)) { - TypeWriter.Logger.Info("Found existing v6 installation, removing cache folder.") - RemoveFileOrFolder(`${InstallLocation()}/Cache`) - } - - FS.mkdirSync(`${InstallLocation()}/Cache`) - FS.writeFileSync(`${InstallLocation()}/InstallationDirectory`, "") - const ExecutablePath = `${InstallLocation()}/typewriter${TypeWriter.OS == "win32" ? ".exe" : ""}` - FS.copySync(process.argv0, ExecutablePath) - - function RemoveLineFromFile(FilePath, RmLine) { - if (FS.existsSync(FilePath) == false) { - return - } - const FileData = FS.readFileSync(FilePath, "utf-8") - const SplitLines = FileData.split("\n") - for (const LineNr in SplitLines) { - const Line = SplitLines[LineNr] - if (Line == RmLine) { - delete SplitLines[LineNr] - } - } - if (SplitLines.filter(function (S) { return S != "" }).length == 0) { - FS.writeFileSync(FilePath, "", "utf-8") - return - } - FS.writeFileSync(FilePath, SplitLines.join("\n"), "utf-8") - } - - function AddLineToFile(FilePath, AddLine) { - if (FS.existsSync(FilePath) == false) { - FS.writeFileSync(FilePath, "", "utf-8") - } - const FileData = FS.readFileSync(FilePath, "utf-8") - const SplitLines = FileData.split("\n") - var LineFound = false - - for (const LineNr in SplitLines) { - const Line = SplitLines[LineNr] - if (Line == AddLine) { - LineFound = true - break - } - } - - if (LineFound == false) { - SplitLines.push(AddLine) - } - FS.writeFileSync(FilePath, SplitLines.join("\n"), "utf-8") - } - - if (TypeWriter.OS != "win32") { - RemoveLineFromFile(`${process.env.HOME}/.bash_profiles`, `alias TypeWriter="'${process.env.HOME}/Library/Application Support/TypeWriter//TypeWriter'"`) - RemoveLineFromFile(`${process.env.HOME}/.bash_profiles`, `alias TypeWriter="'${InstallLocation()}/TypeWriter'"`) - - RemoveLineFromFile(`${process.env.HOME}/.zshenv`, `. ~/.bash_profiles`) - - AddLineToFile(`${process.env.HOME}/.bash_profiles`, `alias TypeWriter="'${InstallLocation()}/TypeWriter'" #This line was added by TypeWriter v6 https://github.com/Dot-lua/TypeWriter/`) - AddLineToFile(`${process.env.HOME}/.bash_profiles`, `alias typewriter="'${InstallLocation()}/TypeWriter'" #This line was added by TypeWriter v6 https://github.com/Dot-lua/TypeWriter/`) - AddLineToFile(`${process.env.HOME}/.zshenv`, `alias TypeWriter="'${InstallLocation()}/TypeWriter'" #This line was added by TypeWriter v6 https://github.com/Dot-lua/TypeWriter/`) - AddLineToFile(`${process.env.HOME}/.zshenv`, `alias typewriter="'${InstallLocation()}/TypeWriter'" #This line was added by TypeWriter v6 https://github.com/Dot-lua/TypeWriter/`) - } else { - FS.writeFileSync( - `${process.env.LOCALAPPDATA}/Microsoft/WindowsApps/TypeWriter.bat`, - `@echo off\n${process.env.APPDATA}/.TypeWriter/TypeWriter.exe %*` - ) - } -} \ No newline at end of file diff --git a/TypeWriter/Installer/PostInstall/NodeBinary.js b/TypeWriter/Installer/PostInstall/NodeBinary.js deleted file mode 100644 index e4f7080..0000000 --- a/TypeWriter/Installer/PostInstall/NodeBinary.js +++ /dev/null @@ -1,71 +0,0 @@ -const FS = require("fs-extra") -const FSHelpers = require("../../Lib/FSHelpers") -const Path = require("path") -const Unzip = require("extract-zip") -const Tar = require("tar") -const Fetch = require("node-fetch") - -const NodeBinariesFolder = `${TypeWriter.Folder}/Binaries/Node/` - -function GetStoredNodeVersion() { - var StoredNodeVersion - try { - StoredNodeVersion = FS.readFileSync(`${NodeBinariesFolder}/Version.txt`) - } catch (error) { - StoredNodeVersion = "Unknown" - } - return StoredNodeVersion -} - -module.exports = async function () { - const NodeVersion = process.versions.node - const StoredNodeVersion = GetStoredNodeVersion() - - TypeWriter.Logger.Debug(`Node version: ${NodeVersion} Stored: ${StoredNodeVersion}`) - - if (NodeVersion == StoredNodeVersion) { return } - - FS.removeSync(NodeBinariesFolder) - FS.ensureDirSync(NodeBinariesFolder) - - const DownloadFileName = `node-v${NodeVersion}-${TypeWriter.OS == "win32" ? "win" : TypeWriter.OS}-${process.arch}.${TypeWriter.OS == "win32" ? "zip" : "tar.gz"}` - const DownloadLink = `https://nodejs.org/dist/v${NodeVersion}/${DownloadFileName}` - console.log(DownloadFileName, DownloadLink) - - TypeWriter.Logger.Information(`Downloading Node.js...`) - - const Download = await Fetch(DownloadLink) - console.log(Download) - const DownloadedFileName = `${NodeBinariesFolder}/Node.${TypeWriter.OS == "win32" ? "zip" : "tar.gz"}` - - FS.writeFileSync(DownloadedFileName, await Download.buffer()) - - if (TypeWriter.OS != "win32") { - await Tar.x( - { - file: DownloadedFileName, - cwd: NodeBinariesFolder - } - ) - } else { - await Unzip( - DownloadedFileName, - { - dir: NodeBinariesFolder - } - ) - } - - const UnzippedFolder = Path.join( - NodeBinariesFolder, - FS.readdirSync(NodeBinariesFolder).find(F => !F.endsWith(".zip") && !F.endsWith(".tar.gz")) - ) - - FSHelpers.MoveFilesInFolder( - UnzippedFolder, - NodeBinariesFolder - ) - - FS.removeSync(UnzippedFolder) - -} \ No newline at end of file diff --git a/TypeWriter/Lib/BuildHelper.js b/TypeWriter/Lib/BuildHelper.js deleted file mode 100644 index d3b2fec..0000000 --- a/TypeWriter/Lib/BuildHelper.js +++ /dev/null @@ -1,195 +0,0 @@ -const BuildHelper = {} - -const RandomString = require("randomstring") -const FS = require("fs-extra") -const Path = require("path") -const KlawSync = require('klaw-sync') -const Tar = require("tar") -const DependencyFormatter = TypeWriter.PackageManagers.DependencyFormatter - -const BuildCacheFolder = `${TypeWriter.Folder}/Cache/BuildCache/` - -BuildHelper.CanBuild = function (Folder) { - return FS.existsSync(`${Folder}/package.info.json`) && FS.existsSync(`${Folder}/resources`) -} - -BuildHelper.GetBuildFolder = function (BuildId) { - return Path.normalize(`${BuildCacheFolder}/${BuildId}/`) -} - -BuildHelper.Build = function (Folder, Branch) { - const BranchFolder = `${Folder}/${Branch}/` - if (!FS.existsSync(BranchFolder)) { TypeWriter.Logger.Error("The selected branch is not valid."); return false } - const BuildId = RandomString.generate(32) - const BuildFolder = this.GetBuildFolder(BuildId) - - if (!this.CanBuild(BranchFolder)) { TypeWriter.Logger.Error("You need a valid package.info.json and resource folder to build."); return false } - - FS.mkdirSync(BuildFolder) - FS.mkdirSync(`${BuildFolder}/resources/`) - - FS.ensureDirSync(`${BuildFolder}/js/`) - FS.ensureDirSync(`${BuildFolder}/lua/`) - FS.ensureDirSync(`${BuildFolder}/resources/`) - - { // Update Dependencies - var PackageData - try { - PackageData = FS.readJSONSync(`${BranchFolder}/package.info.json`) - } catch (E) { - TypeWriter.Logger.Error(`Could not read package.info.json (${E})`) - return false - } - var NeedsWrite = false - - for (const DependencyIndex in PackageData.Dependencies) { - var Dependency = PackageData.Dependencies[DependencyIndex] - - if (typeof Dependency == "object") { - const DependencySource = Dependency.Source.toLowerCase() - var DependencyAuthor - var DependencyName - if (Dependency.Package.includes("/")) { - DependencyAuthor = Dependency.Package.split("/")[0].substring(1) - DependencyName = Dependency.Package.split("/")[1] - } else { - DependencyName = Dependency.Package - } - - NeedsWrite = true - PackageData.Dependencies[DependencyIndex] = DependencyFormatter.FormatDependency( - { - Source: DependencySource, - Author: DependencyAuthor, - Name: DependencyName, - Version: Dependency.Version - } - ) - - Dependency = PackageData.Dependencies[DependencyIndex] - } - - if (!TypeWriter.PackageManagers.PackageExists(Dependency)) { - TypeWriter.Logger.Error(`Package ${Dependency} does not exist. Please check your package.info.json file.`) - return false - } - - if (!TypeWriter.PackageManagers.HasVersion(Dependency)) { - TypeWriter.Logger.Warning(`Missing version for package ${Dependency}, attempting to find latest version`) - NeedsWrite = true - const ParsedDependency = DependencyFormatter.ParseDependency(PackageData.Dependencies[DependencyIndex]) - ParsedDependency.Version = TypeWriter.PackageManagers.LatestPackageVersion(Dependency) - PackageData.Dependencies[DependencyIndex] = DependencyFormatter.FormatDependency(ParsedDependency) - } - } - - if (NeedsWrite) { - TypeWriter.Logger.Warning(`Updating values in package.info.json`) - FS.writeJSONSync(`${BranchFolder}/package.info.json`, PackageData, { spaces: "\t" }) - } - FS.writeJSONSync(`${BuildFolder}/package.info.json`, PackageData, {spaces: "\t"}) - } - - const CompiledCode = {} - function CodeScan(ScanFolder, Ext) { - FS.ensureDirSync(ScanFolder) - ScanFolder = Path.resolve(ScanFolder) - TypeWriter.Logger.Debug("Scanning " + ScanFolder + " for " + Ext + " files") - const Files = KlawSync(ScanFolder, { nodir: true }) - - for (const FileInformation of Files) { - const FilePath = FileInformation.path - if (Path.extname(FilePath) != `.${Ext}`) { - TypeWriter.Logger.Error(`Found ${FilePath} but file extension needs to be .${Ext}, skipping.`) - continue - } - var CodePath = Path.normalize(FilePath).split(ScanFolder)[1].replaceAll("\\", "/").replaceAll("/", ".") - if (CodePath.startsWith(".")) { CodePath = CodePath.substring(1) } - CodePath = CodePath.substring(0, CodePath.length - Ext.length - 1) - var ParentCodePath = CodePath.split(".") - delete ParentCodePath[ParentCodePath.length - 1] - ParentCodePath = ParentCodePath.join(".") - ParentCodePath = ParentCodePath.substring(0, ParentCodePath.length - 1) - - TypeWriter.Logger.Debug(`Found ${Ext} file ${CodePath} at ${FilePath}`) - CompiledCode[CodePath] = { - Type: Ext, - Code: encodeURIComponent(FS.readFileSync(FilePath, "utf-8")) - } - - if (Path.basename(FilePath, `.${Ext}`) == "Main") { - CompiledCode[ParentCodePath] = { - Type: "Redirect", - Path: CodePath - } - } else if (Path.basename(FilePath, `.${Ext}`) == "Index") { - CompiledCode[ParentCodePath] = { - Type: "Redirect", - Path: CodePath - } - } - } - } - - CodeScan(`${BranchFolder}/lua/`, "lua") - CodeScan(`${BranchFolder}/js/`, "js") - - FS.writeJSONSync( - `${BuildFolder}/Code.json`, - CompiledCode, - { - spaces: "\t" - } - ) - - const ResourceIndex = [] - const ResourceFolder = Path.resolve(`${BranchFolder}/resources/`) - for (const File of KlawSync(ResourceFolder, { nodir: true })) { - const FilePath = File.path - const ResourceFilePath = Path.normalize(FilePath.split(ResourceFolder)[1]).replaceAll("\\", "/") - const DestFilePath = `${BuildFolder}/resources/${ResourceFilePath}` - TypeWriter.Logger.Debug(`Found resource ${ResourceFilePath} at ${FilePath}`) - FS.ensureDirSync(Path.dirname(DestFilePath)) - FS.copyFileSync(FilePath, DestFilePath) - ResourceIndex.push(ResourceFilePath) - } - - FS.writeJSONSync( - `${BuildFolder}/ResourceIndex.json`, - ResourceIndex, - { - spaces: "\t" - } - ) - - return BuildId -} - -BuildHelper.CompressBuild = function (BuildId, OutputFolder) { - if (BuildId == false) { - TypeWriter.Logger.Error("Looks like the build failed, not compressing") - return false - } - - const BuildFolder = this.GetBuildFolder(BuildId) - const OutputFile = Path.join(OutputFolder, FS.readJSONSync(`${BuildFolder}/package.info.json`).Id) + ".twr" - TypeWriter.Logger.Debug(`Outputting to ${OutputFile} in ${BuildFolder}`) - Tar.create( - { - file: OutputFile, - cwd: BuildFolder, - sync: true, - noMtime: true, - portable: true - }, - FS.readdirSync(BuildFolder) - ) -} - -BuildHelper.CleanupBuild = function (BuildId) { - const BuildFolder = this.GetBuildFolder(BuildId) - TypeWriter.Logger.Debug(`Cleaning up ${BuildFolder}`) - FS.rmSync(BuildFolder, { recursive: true, force: true }); -} - -module.exports = BuildHelper \ No newline at end of file diff --git a/TypeWriter/Lib/PackageManagers/DependencyFormatter.js b/TypeWriter/Lib/DependencyParser.js similarity index 90% rename from TypeWriter/Lib/PackageManagers/DependencyFormatter.js rename to TypeWriter/Lib/DependencyParser.js index 5c59473..3532a20 100644 --- a/TypeWriter/Lib/PackageManagers/DependencyFormatter.js +++ b/TypeWriter/Lib/DependencyParser.js @@ -1,4 +1,4 @@ -module.exports.ParseDependency = function (DependencyString) { +module.exports.Parse = function (DependencyString) { const Source = DependencyString.split("+")[0] var Author var Name @@ -33,7 +33,7 @@ module.exports.ParseDependency = function (DependencyString) { } } -module.exports.FormatDependency = function (DependencyObject) { +module.exports.Format = function (DependencyObject) { var DependencyString = `${DependencyObject.Source}+` if (DependencyObject.Author) { DependencyString += `${DependencyObject.Author}/` } DependencyString += DependencyObject.Name diff --git a/TypeWriter/Lib/FetchFile.js b/TypeWriter/Lib/FetchFile.js new file mode 100644 index 0000000..552657f --- /dev/null +++ b/TypeWriter/Lib/FetchFile.js @@ -0,0 +1,18 @@ +const Path = require("path") +const FS = require("fs-extra") +const Fetch = require("node-fetch") + +async function FetchFile(Url, File) { + try { + if (FS.existsSync(File)) { return } + FS.mkdirpSync(Path.dirname(File)) + const Response = await Fetch(Url) + const Buffer = await Response.buffer() + FS.writeFileSync(File, Buffer, "utf8") + } catch (error) { + TypeWriter.Logger.Warning(`Failed to fetch '${Url}' (${error})`) + return await FetchFile(Url, File) + } +} + +module.exports = FetchFile \ No newline at end of file diff --git a/TypeWriter/Lib/FetchJson.js b/TypeWriter/Lib/FetchJson.js new file mode 100644 index 0000000..2add232 --- /dev/null +++ b/TypeWriter/Lib/FetchJson.js @@ -0,0 +1,9 @@ +const NodeFetch = require("node-fetch") + +async function JsonRequest(Url) { + const Response = await NodeFetch(Url) + const Json = await Response.json() + return [Response, Json] +} + +module.exports = JsonRequest \ No newline at end of file diff --git a/TypeWriter/Lib/JsonRequest.js b/TypeWriter/Lib/JsonRequest.js deleted file mode 100644 index 2abf5de..0000000 --- a/TypeWriter/Lib/JsonRequest.js +++ /dev/null @@ -1,23 +0,0 @@ -const Fetch = require("sync-fetch") -const Cache = {} - -function JsonRequest(Url) { - if (Cache[Url]) { - return Cache[Url] - } else { - const Response = Fetch( - Url - ) - var Data = false - try { - Data = Response.json() - } catch {} - Cache[Url] = { - Data: Data, - Response: Response - } - return Cache[Url] - } -} - -module.exports = JsonRequest \ No newline at end of file diff --git a/TypeWriter/Lib/LoadEnvoirment/Index.js b/TypeWriter/Lib/LoadEnvoirment/Index.js index ecfbb97..6d9490e 100644 --- a/TypeWriter/Lib/LoadEnvoirment/Index.js +++ b/TypeWriter/Lib/LoadEnvoirment/Index.js @@ -3,7 +3,10 @@ const Path = require("path") module.exports = async function(ExecuteFolder) { await require("./Load.js")(ExecuteFolder) - TypeWriter.Lua.LoadFile( - Path.join(__dirname, "./Load.lua") - ) + if (!global.TypeWriterDisableLua) { + TypeWriter.Lua.LoadFile( + Path.join(__dirname, "./Load.lua") + ) + } + } \ No newline at end of file diff --git a/TypeWriter/Lib/LoadEnvoirment/Languages/Index.js b/TypeWriter/Lib/LoadEnvoirment/Languages/Index.js new file mode 100644 index 0000000..1fa669a --- /dev/null +++ b/TypeWriter/Lib/LoadEnvoirment/Languages/Index.js @@ -0,0 +1,6 @@ +module.exports = async function LoadLanguages(Parent) { + Parent.JavaScript = await require("./JavaScript/Index.js")() + if (!global.TypeWriterDisableLua) { + Parent.Lua = await require("./Lua/Index.js")() + } +} \ No newline at end of file diff --git a/TypeWriter/Lib/LoadEnvoirment/Languages/JavaScript/Index.js b/TypeWriter/Lib/LoadEnvoirment/Languages/JavaScript/Index.js new file mode 100644 index 0000000..f87be70 --- /dev/null +++ b/TypeWriter/Lib/LoadEnvoirment/Languages/JavaScript/Index.js @@ -0,0 +1,42 @@ +const RequireFromString = require("require-from-string") + +module.exports = async function() { + return { + LoadFile: async function (FilePath) { + const FileData = FS.readFileSync(FilePath, "utf8") + return RequireFromString(FileData, Path.normalize(FilePath)) + }, + // LoadFileAsync: async function (FilePath) { + // return this.LoadFile(FilePath) + // }, + + LoadString: async function (String, Name) { + return RequireFromString(String, Name) + }, + // LoadStringSync: function (String, Name) { + // return RequireFromString(String, Name) + // }, + + LoadStringWrapped: async function (String, Name) { + const WrappedCodeData = `/* Wrapped by LoadStringWrapped (TypeWriter) */module.exports = (async function WrappedImport() { \n${String}\nreturn module.exports })` + const WrappedImport = await TypeWriter.JavaScript.LoadString(WrappedCodeData, Name) + return await WrappedImport() + }, + + + //Operators + New: function (Class, ...Args) { + return new Class(...Args) + }, + + TypeOf: function (Object) { + return typeof Object + }, + + InstanceOf: function (Object, Class) { + return Object instanceof Class + }, + + Global: globalThis + } +} \ No newline at end of file diff --git a/TypeWriter/Lib/LoadEnvoirment/Languages/Lua/Index.js b/TypeWriter/Lib/LoadEnvoirment/Languages/Lua/Index.js new file mode 100644 index 0000000..ad62b3f --- /dev/null +++ b/TypeWriter/Lib/LoadEnvoirment/Languages/Lua/Index.js @@ -0,0 +1,38 @@ +const FS = require("fs-extra") + +const WasMoon = require("wasmoon") + + +module.exports = async function() { + const LuaFactory = new WasMoon.LuaFactory() + const LuaEnvoirment = await LuaFactory.createEngine( + { + enableProxy: true, + injectObjects: true, + openStandardLibs: true, + traceAllocations: false + } + ) + LuaEnvoirment.global.registerTypeExtension(10, new (require("./LegacyClassFix.js"))) + LuaEnvoirment.global.set("TypeWriter", TypeWriter) + + return { + Envoirment: LuaEnvoirment, + + LoadFile: async function (FilePath) { + const FileData = await FS.promises.readFile(FilePath, "utf8") + return await LuaEnvoirment.doString(FileData) + }, + LoadFileSync: function (FilePath) { + const FileData = FS.readFileSync(FilePath, "utf8") + return LuaEnvoirment.doStringSync(FileData) + }, + + LoadString: async function (String, Name) { + return await LuaEnvoirment.doString(String) + }, + LoadStringSync: function (String, Name) { + return LuaEnvoirment.doStringSync(String) + } + } +} \ No newline at end of file diff --git a/TypeWriter/Lib/LoadEnvoirment/LegacyClassFix.js b/TypeWriter/Lib/LoadEnvoirment/Languages/Lua/LegacyClassFix.js similarity index 100% rename from TypeWriter/Lib/LoadEnvoirment/LegacyClassFix.js rename to TypeWriter/Lib/LoadEnvoirment/Languages/Lua/LegacyClassFix.js diff --git a/TypeWriter/Lib/LoadEnvoirment/Load.js b/TypeWriter/Lib/LoadEnvoirment/Load.js index 202d60f..2bfa1d0 100644 --- a/TypeWriter/Lib/LoadEnvoirment/Load.js +++ b/TypeWriter/Lib/LoadEnvoirment/Load.js @@ -2,7 +2,6 @@ module.exports = async function (ExecuteFolder) { { // Global Vars TypeWriter.ExecuteFolder = ExecuteFolder - TypeWriter.LoadedPackages = {} Error.stackTraceLimit = Infinity } @@ -26,93 +25,17 @@ module.exports = async function (ExecuteFolder) { } { // Runtime functions - const RuntimeHelper = require("../RuntimeHelper.js") - TypeWriter.GetPackagePath = RuntimeHelper.GetPackagePath - - TypeWriter.LoadFile = RuntimeHelper.LoadFile - - TypeWriter.Import = RuntimeHelper.Import - globalThis.Import = RuntimeHelper.Import - TypeWriter.ImportAsync = RuntimeHelper.ImportAsync - globalThis.ImportAsync = RuntimeHelper.ImportAsync - - TypeWriter.LoadEntrypoint = RuntimeHelper.LoadEntrypoint - TypeWriter.LoadEntrypointAsync = RuntimeHelper.LoadEntrypointAsync - - TypeWriter.PackageManager = require("../PackageManager") - TypeWriter.ResourceManager = require("../ResourceManager") + globalThis.Import = async function ProxiedImport(ImportPath) { + return await TypeWriter.PackageManager.Import(ImportPath) + } + TypeWriter.LoadPackage = async function ProxiedLoadPackage(FilePath) { + return await TypeWriter.PackageManager.LoadPackage(FilePath) + } + TypeWriter.LoadFile = TypeWriter.LoadPackage } { // Language globals - - const FS = require("fs-extra") - const Path = require("path") - const RequireFromString = require("require-from-string") - - const WasMoon = require("wasmoon") - const LuaFactory = new WasMoon.LuaFactory() - const LuaEnvoirment = await LuaFactory.createEngine( - { - enableProxy: true, - injectObjects: true, - openStandardLibs: true, - traceAllocations: false - } - ) - LuaEnvoirment.global.registerTypeExtension(10, new (require("./LegacyClassFix.js"))) - - TypeWriter.Lua = { - Envoirment: LuaEnvoirment, - LoadFile: function (FilePath) { - const FileData = FS.readFileSync(FilePath, "utf8") - return LuaEnvoirment.doStringSync(FileData) - }, - LoadFileAsync: async function (FilePath) { - const FileData = await FS.promises.readFile(FilePath, "utf8") - return await LuaEnvoirment.doString(FileData) - }, - - LoadString: function (String, Name) { - return LuaEnvoirment.doStringSync(String) - }, - LoadStringAsync: async function (String, Name) { - return await LuaEnvoirment.doString(String) - } - } - TypeWriter.Lua.Envoirment.global.set("TypeWriter", TypeWriter) - - TypeWriter.JavaScript = { - LoadFile: function (FilePath) { - const FileData = FS.readFileSync(FilePath, "utf8") - return RequireFromString(FileData, Path.normalize(FilePath)) - }, - LoadFileAsync: async function (FilePath) { - return this.LoadFile(FilePath) - }, - - LoadString: function (String, Name) { - return RequireFromString(String, Name) - }, - LoadStringAsync: async function (String, Name) { - return this.LoadString(String, Name) - }, - - //Operators - New: function (Class, ...Args) { - return new Class(...Args) - }, - - TypeOf: function (Object) { - return typeof Object - }, - - InstanceOf: function (Object, Class) { - return Object instanceof Class - }, - - Global: globalThis - - } + await require("./Languages/Index.js")(TypeWriter) } { // Load Require diff --git a/TypeWriter/Lib/Logger.js b/TypeWriter/Lib/Logger.js deleted file mode 100644 index 39f1db5..0000000 --- a/TypeWriter/Lib/Logger.js +++ /dev/null @@ -1,66 +0,0 @@ -const Logger = {} - -const Colors = require("colors/safe") -const LogLevel = Number(process.env.TYPEWRITER_LOGLEVEL) || 2 -const Levels = [ - { - Label: "[ERROR] ", - Color: "red" - }, - { - Label: "[WARNING] ", - Color: "yellow" - }, - { - Label: "[INFORMATION]", - Color: "brightGreen" - }, - { - Label: "[DEBUG] ", - Color: "cyan" - } -] - -function Pad(num, size) { - var s = "00" + num; - return s.substring(s.length-size); -} - -Logger.Log = function(Level, Message) { - if (Level > LogLevel) {return} - - const LevelInfo = Levels[Level] - const Time = new Date() - console.log( - `[${Time.getFullYear()}-${Pad(Time.getMonth() + 1, 2)}-${Pad(Time.getDate(), 2)} ${Pad(Time.getHours(), 2)}:${Pad(Time.getMinutes(), 2)}:${Pad(Time.getSeconds(), 2)}] ${Colors.bold(Colors[LevelInfo.Color](LevelInfo.Label))}: ${Message}` - ) -} - -Logger.Debug = function(Message) { - this.Log(3, Message) -} - - -Logger.Info = function(Message) { - this.Log(2, Message) -} - -Logger.Information = function(Message) { - this.Info(Message) -} - - -Logger.Warn = function(Message) { - this.Log(1, Message) -} - -Logger.Warning = function(Message) { - this.Warn(Message) -} - - -Logger.Error = function(Message) { - this.Log(0, Message) -} - -module.exports = Logger \ No newline at end of file diff --git a/TypeWriter/Lib/PackageManager.js b/TypeWriter/Lib/PackageManager.js deleted file mode 100644 index 19d9900..0000000 --- a/TypeWriter/Lib/PackageManager.js +++ /dev/null @@ -1,31 +0,0 @@ -const PackageManager = {} - -PackageManager.GetPackageInfo = function (Id) { - return TypeWriter.LoadedPackages[Id].Package -} - -PackageManager.GetPackageEntrypoints = function (Id) { - return this.GetPackageInfo(Id).Entrypoints -} - -PackageManager.ListPackageIds = function () { - return Object.keys(TypeWriter.LoadedPackages) -} - -PackageManager.IsPackageLoaded = function (Id) { - return this.ListPackageIds().includes(Id) -} - -PackageManager.ListDependencyObjects = function(Id) { - const PackageData = TypeWriter.LoadedPackages[Id].Package - const Objects = [] - - for (const DependencyString of PackageData.Dependencies) { - const DependencyObject = TypeWriter.PackageManagers.TryParse(DependencyString) - Objects.push(DependencyObject) - } - - return Objects -} - -module.exports = PackageManager \ No newline at end of file diff --git a/TypeWriter/Lib/PackageManagers/Index.js b/TypeWriter/Lib/PackageManagers/Index.js deleted file mode 100644 index dd2fbbc..0000000 --- a/TypeWriter/Lib/PackageManagers/Index.js +++ /dev/null @@ -1,56 +0,0 @@ - - -module.exports = { - Managers: { - NPM: new (require("./Managers/NPM.js")), - LIT: new (require("./Managers/LIT.js")), - }, - - DependencyFormatter: require("./DependencyFormatter.js"), - TryParse: function (Dependency) { - if (typeof Dependency == "object") { - return Dependency - } else { - return this.DependencyFormatter.ParseDependency(Dependency) - } - }, - - GetManager: function (Manager) { - if (this.Managers[Manager.toLowerCase()]) { - return this.Managers[Manager.toLowerCase()] - } else if (this.Managers[Manager.toUpperCase()]) { - return this.Managers[Manager.toUpperCase()] - } else { - return null - } - }, - - PackageExists: function (Dependency) { - Dependency = this.TryParse(Dependency) - const Manager = this.GetManager(Dependency.Source) - return Manager.PackageExists(Dependency) - }, - - LatestPackageVersion: function (Dependency) { - Dependency = this.TryParse(Dependency) - const Manager = this.GetManager(Dependency.Source) - return Manager.LatestPackageVersion(Dependency) - }, - - PackageFolder: function (Dependency) { - Dependency = this.TryParse(Dependency) - const Manager = this.GetManager(Dependency.Source) - return Manager.PackageFolder(Dependency) - }, - - LoadPackage: function (Dependency, ExecuteDirectory) { - Dependency = this.TryParse(Dependency) - const Manager = this.GetManager(Dependency.Source) - return Manager.LoadPackage(Dependency, ExecuteDirectory) - }, - - HasVersion: function (Dependency) { - Dependency = this.TryParse(Dependency) - return Dependency.Version != undefined || Dependency.Version != null - } -} \ No newline at end of file diff --git a/TypeWriter/Lib/PackageManagers/Managers/LIT.js b/TypeWriter/Lib/PackageManagers/Managers/LIT.js deleted file mode 100644 index 8e88157..0000000 --- a/TypeWriter/Lib/PackageManagers/Managers/LIT.js +++ /dev/null @@ -1,7 +0,0 @@ -class LIT { - constructor() { - - } -} - -module.exports = LIT \ No newline at end of file diff --git a/TypeWriter/Lib/PackageManagers/Managers/NPM.js b/TypeWriter/Lib/PackageManagers/Managers/NPM.js deleted file mode 100644 index 9ecabd8..0000000 --- a/TypeWriter/Lib/PackageManagers/Managers/NPM.js +++ /dev/null @@ -1,212 +0,0 @@ -const FS = require("fs-extra") -const FSHelpers = require("../../FSHelpers") -const JsonRequest = require("../../JsonRequest") -const Fetch = require("sync-fetch") -const Tar = require("tar") -const Path = require("path") - -const CacheFolder = `${TypeWriter.Folder}/Cache/ModuleCache/NPM/` -const ModulesFolder = `${CacheFolder}/Modules/` -const ModuleTarsFolder = `${CacheFolder}/ModuleTars/` -const UnpackFolder = `${CacheFolder}/Unpack/` -const MainFileName = require("require-main-filename")() -const InstallScriptNames = ["preinstall", "install", "postinstall"] - -function DownloadLinkFromPackage(Package) { - var AuthorLessName = Package.name - if (Package.name.startsWith("@")) { - AuthorLessName = Package.name.split("/")[1] - } - return `https://registry.npmjs.org/${Package.name}/-/${AuthorLessName}-${Package.version}.tgz` -} - -function MoveFrom(FromPath, ToPath) { - for (const FileName of FS.readdirSync(FromPath)) { - while (true) { - try { - FS.moveSync(`${FromPath}/${FileName}`, `${ToPath}/${FileName}`) - break - } catch (error) { - TypeWriter.Logger.Warning(`Failed to move ${FileName} from ${FromPath} to ${ToPath}`) - } - } - } -} - -class NPM { - constructor() { - - } - - GetPackageFolder(Dependency, Version = true, CreateFolder = true) { - var FullName = Dependency.FullName - if (Dependency.Author) { - FullName = `@${Dependency.FullName}` - } - - var ModuleFolder = `${ModulesFolder}/${FullName}` - - if (CreateFolder) { - FS.ensureDirSync(`${ModuleFolder}/Versions/`) - } - if (Version) { - ModuleFolder += `/Versions/${Dependency.Version}` - } - if (CreateFolder) { - FS.ensureDirSync(ModuleFolder) - } - - return ModuleFolder - } - - PackageCached(Dependency) { - return FS.existsSync(this.GetPackageFolder(Dependency, true, false)) - } - - PackageExists(Dependency) { - if (FS.existsSync(this.GetPackageFolder(Dependency, false, false))) { - return true - } - - const Exists = JsonRequest(`https://cdn.jsdelivr.net/npm/${Dependency.AtFullName}/package.json`).Response.status == 200 - if (Exists) { - this.GetPackageFolder(Dependency, false, true) - } - return Exists - } - - LatestPackageVersion(Dependency) { - return JsonRequest(`https://cdn.jsdelivr.net/npm/${Dependency.AtFullName}/package.json`).Data.version - } - - PackageInformation(Dependency) { - return JsonRequest(`https://cdn.jsdelivr.net/npm/${Dependency.AtFullName}@${Dependency.Version}/package.json`).Data - } - - DownloadPackage(Dependency, ParentPackage = "None") { - var PackageFolder = this.GetPackageFolder(Dependency, true, false) - - if (FS.existsSync(PackageFolder)) { - TypeWriter.Logger.Debug(`Package ${Dependency.String} already downloaded`) - return PackageFolder - } - - const PackageInfo = this.PackageInformation(Dependency) - Dependency.Version = PackageInfo.version - var PackageFolder = this.GetPackageFolder(Dependency, true, false) - - if (FS.existsSync(PackageFolder)) { - TypeWriter.Logger.Debug(`Package ${Dependency.String} already downloaded`) - return PackageFolder - } - FS.mkdirpSync(PackageFolder) - - var NodeModulesFolder = `${PackageFolder}/node_modules/` - FS.ensureDirSync(NodeModulesFolder) - - for (const DependencyName in PackageInfo.dependencies) { - const DependencyVersion = PackageInfo.dependencies[DependencyName] - const DependencyData = { - Source: "npm", - Version: DependencyVersion - } - if (DependencyName.startsWith("@")) { - DependencyData.Author = DependencyName.split("/")[0].substring(1) - DependencyData.Name = DependencyName.split("/")[1] - DependencyData.FullName = `${DependencyData.Author}/${DependencyData.Name}` - DependencyData.AtFullName = `@${DependencyData.Author}/${DependencyData.Name}` - } else { - DependencyData.Name = DependencyName - DependencyData.FullName = DependencyName - DependencyData.AtFullName = DependencyName - } - DependencyData.String = TypeWriter.PackageManagers.DependencyFormatter.FormatDependency(DependencyData) - - const DependencyFolder = this.DownloadPackage(DependencyData, Dependency.FullName) - - if (DependencyData.Author) { - FS.ensureDirSync(`${NodeModulesFolder}/@${DependencyData.Author}`) - } - FS.symlinkSync( - DependencyFolder, - `${NodeModulesFolder}/${DependencyData.AtFullName}`, - TypeWriter.OS == "win32" ? 'junction' : 'dir' - ) - } - - const DownloadLink = DownloadLinkFromPackage(PackageInfo) - - const TarFile = `${ModuleTarsFolder}/${Dependency.AtFullName}/${Dependency.Version}.tgz` - if (!FS.existsSync(TarFile)) { - FS.ensureDirSync(`${ModuleTarsFolder}/${Dependency.AtFullName}`) - TypeWriter.Logger.Information(`Downloading ${Dependency.FullName}@${Dependency.Version}`) - const DownloadedTar = Fetch(DownloadLink).buffer() - FS.writeFileSync(TarFile, DownloadedTar) - } - - TypeWriter.Logger.Information(`Unpacking ${Dependency.FullName}@${Dependency.Version}`) - Tar.extract( - { - file: TarFile, - sync: true, - cwd: UnpackFolder, - preserveOwner: false - } - ) - - const MoveFolder = FSHelpers.FindDown(UnpackFolder, "package.json") - TypeWriter.Logger.Information(`Moving ${Dependency.FullName}@${Dependency.Version}`) - MoveFrom(MoveFolder, PackageFolder) - - var InstallScript - for (const ScriptName of InstallScriptNames) { - if ((PackageInfo.scripts || {})[ScriptName]) { - InstallScript = PackageInfo.scripts[ScriptName] - } - } - - if (InstallScript) { - const SplitScript = InstallScript.split(" ") - if (SplitScript[0] == "node") { - TypeWriter.Logger.Information(`Running install script for ${Dependency.FullName}@${Dependency.Version}`) - require("child_process").execFileSync( - TypeWriter.Executable, - [MainFileName, "runscript", "-i", Path.join(PackageFolder, SplitScript[1])], - { - cwd: `${PackageFolder}`, - stdio: "inherit" - } - ) - } - } - - return PackageFolder - - } - - PackageFolder(Dependency) { - return this.GetPackageFolder(Dependency, true, false) - } - - LoadPackage(Dependency, ExecuteDirectory) { - const PackageFolder = this.GetPackageFolder(Dependency, true, false) - if (!FS.existsSync(PackageFolder)) { - this.DownloadPackage(Dependency) - } - - const ModulesFolder = `${ExecuteDirectory}/node_modules/` - FS.ensureDirSync(ModulesFolder) - if (Dependency.Author) { - FS.ensureDirSync(`${ModulesFolder}/@${Dependency.Author}`) - } - - FS.symlinkSync( - PackageFolder, - `${ModulesFolder}/${Dependency.AtFullName}`, - TypeWriter.OS == "win32" ? 'junction' : 'dir' - ) - - } -} - -module.exports = NPM \ No newline at end of file diff --git a/TypeWriter/Lib/RandomString.js b/TypeWriter/Lib/RandomString.js new file mode 100644 index 0000000..9ebb7f8 --- /dev/null +++ b/TypeWriter/Lib/RandomString.js @@ -0,0 +1,9 @@ +function RandomString(Length) { + let String = "" + while (String.length < Length) { + String += Math.random().toString(36).substring(2) + } + return String.substring(0, Length) +} + +module.exports = RandomString \ No newline at end of file diff --git a/TypeWriter/Lib/Require.js b/TypeWriter/Lib/Require.js index 01b7c97..e5c5e95 100644 --- a/TypeWriter/Lib/Require.js +++ b/TypeWriter/Lib/Require.js @@ -1,4 +1,5 @@ const FS = require("fs-extra") +const FSHelpers = require("./FSHelpers") const Path = require("path") const GetCallerFile = require("get-caller-file") const IsCoreModule = require("is-builtin-module") @@ -6,43 +7,8 @@ const IsCoreModule = require("is-builtin-module") const OriginalRequire = TypeWriter.OriginalRequire const Exts = ["js", "json", "node"] -function FindModuleEntrypoint(Path) { - try { - return require.resolve(Path) - } catch (error) { } - - const PackageData = FS.readJsonSync(Path + "/package.json") - - if (PackageData.main) { - return FindModuleEntrypoint(Path + "/" + PackageData.main) - } - - if (PackageData.exports) { - if (PackageData.exports["."]) { - if (PackageData.exports["."].node) { - return FindModuleEntrypoint(Path + "/" + PackageData.exports["."].node.require) - } - } - } - - if (PackageData.exports) { - if (PackageData.exports["."]) { - return FindModuleEntrypoint(Path + "/" + PackageData.exports["."]) - } - } - -} - -function GetModuleRoot(Path) { - const SplitPath = Path.split("/") - var Index = 0 - - for (const PathPart of SplitPath) { - if (PathPart == "Versions") { break } - Index++ - } - - return SplitPath.splice(0, Index + 2).join("/") + "/" +function GetModuleRoot(ModulePath) { + return FSHelpers.FindUp(ModulePath, "package.json") } function GetCallerInformation(Caller) { @@ -61,7 +27,7 @@ function GetCallerInformation(Caller) { CallerData.IsPackage = CallerData.CallerId != undefined if (CallerData.IsPackage) { - CallerData.PackagePath = TypeWriter.GetPackagePath(CallerData.CallerId) + CallerData.PackagePath = TypeWriter.PackageManager.GetPackage(CallerData.CallerId).ExecuteFolder } else { CallerData.IsModule = true } @@ -69,36 +35,14 @@ function GetCallerInformation(Caller) { return CallerData } -function GeneratePaths(Request, CallerInfo) { - const Paths = [] - - const Root = CallerInfo.IsPackage ? CallerInfo.PackagePath : CallerInfo.ModuleRoot - Paths.push(Path.join(Root, "node_modules", Request)) - - { - var PathStart = 1 - if (Request.startsWith("@")) { - PathStart = 2 - } - - const SplitRequest = Request.split("/") - const PackageName = SplitRequest.splice(0, PathStart).join("/") - const PackagePath = Path.join(Root, "node_modules", PackageName) - Paths.push(Path.join(PackagePath, SplitRequest.join("/"))) - } - - return Paths -} - function Require(Request) { const Caller = GetCallerFile(3) const CallerInfo = GetCallerInformation(Caller) - // console.log(Request, Caller, FS.existsSync(Request)) // console.log( - // Request, - // Caller, - // CallerInfo + // Request, + // Caller, + // CallerInfo // ) if (IsCoreModule(Request)) { // Is it a core module @@ -109,29 +53,18 @@ function Require(Request) { return OriginalRequire(Path.join(Path.dirname(Caller), Request)) } - if (FS.existsSync(Request)) { // Is it a file - return OriginalRequire(Request) + if (CallerInfo.IsPackage) { + const Package = TypeWriter.PackageManager.GetPackage(CallerInfo.CallerId) + return OriginalRequire(Path.join(Package.NodeModulesFolder, Request)) } - const Paths = GeneratePaths(Request, CallerInfo) - - for (const Path of Paths) { - let EntrypointPath - try { - EntrypointPath = FindModuleEntrypoint(Path) - } catch (error) { - continue - } - if (FS.existsSync(EntrypointPath)) { - return OriginalRequire(EntrypointPath) - } + if (CallerInfo.IsModule) { + return OriginalRequire(require.resolve(Request, { paths: [CallerInfo.ModuleRoot] })) } - const Err = new Error("Cannot find module '" + Request + "'") + const Err = new Error("Module not found: " + Request) Err.code = "MODULE_NOT_FOUND" - throw Err - } module.exports = Require \ No newline at end of file diff --git a/TypeWriter/Lib/ResourceManager.js b/TypeWriter/Lib/ResourceManager.js deleted file mode 100644 index ebb9404..0000000 --- a/TypeWriter/Lib/ResourceManager.js +++ /dev/null @@ -1,62 +0,0 @@ -const SingleTar = require("../Lib/SingleTar") -const FS = require("fs-extra") -const Path = require("path") - -const ResourceManager = {} - -function GetPackagePath(Id) { - return `${TypeWriter.ExecuteFolder}/${Id}/` -} - -function ParseResourcePath(Id, ResourcePath) { - if (ResourcePath != undefined) { - return [Id, ResourcePath] - } - const Split = Id.split(":") - return [Split[0], Split[1]] -} - -ResourceManager.ResourceExists = function(Id, ResourcePath) { - var ResourceData = ParseResourcePath(Id, ResourcePath); var Id = ResourceData[0]; var ResourcePath = ResourceData[1]; - if (!TypeWriter.PackageManager.IsPackageLoaded(Id)) { - return false - } - return TypeWriter.LoadedPackages[Id].ResourceIndex.includes(ResourcePath) -} - -ResourceManager.ListResources = function(Id) { - if (!TypeWriter.PackageManager.IsPackageLoaded(Id)) { - return [] - } - return TypeWriter.LoadedPackages[Id].ResourceIndex -} - -ResourceManager.GetRaw = function(Id, ResourcePath) { - var ResourceData = ParseResourcePath(Id, ResourcePath); var Id = ResourceData[0]; var ResourcePath = ResourceData[1]; - if (!this.ResourceExists(Id, ResourcePath)) { - return null - } - return SingleTar(TypeWriter.LoadedPackages[Id].PackagePath, `resources${ResourcePath}`) -} - -ResourceManager.GetJson = function(Id, ResourcePath) { - var ResourceData = ParseResourcePath(Id, ResourcePath); var Id = ResourceData[0]; var ResourcePath = ResourceData[1]; - if (!this.ResourceExists(Id, ResourcePath)) { - return null - } - return JSON.parse(this.GetRaw(Id, ResourcePath)) -} - -ResourceManager.GetFilePath = function(Id, ResourcePath) { - var ResourceData = ParseResourcePath(Id, ResourcePath); var Id = ResourceData[0]; var ResourcePath = ResourceData[1]; - if (!this.ResourceExists(Id, ResourcePath)) { - return null - } - - const OutputPath = `${GetPackagePath(Id)}resources${ResourcePath}` - FS.ensureDirSync(Path.dirname(OutputPath)) - FS.writeFileSync(OutputPath, this.GetRaw(Id, ResourcePath), "binary") - return OutputPath -} - -module.exports = ResourceManager \ No newline at end of file diff --git a/TypeWriter/Registry/Arguments.js b/TypeWriter/Registry/Arguments.js index 4eba81c..9da5eb5 100644 --- a/TypeWriter/Registry/Arguments.js +++ b/TypeWriter/Registry/Arguments.js @@ -103,7 +103,6 @@ const SubParsers = MainParser.add_subparsers( } ) } - { //Runscript Parser const RunScriptParser = SubParsers.add_parser( "runscript", @@ -121,7 +120,12 @@ const SubParsers = MainParser.add_subparsers( ) } -var Parsed = MainParser.parse_known_args() -module.exports.Arguments = Parsed[0] -module.exports.Parser = MainParser -module.exports.Unknown = Parsed[1] \ No newline at end of file +var Argv = process.argv.slice(2) +if (global.TypeWriterReplaceArguments) { + Argv = global.TypeWriterReplaceArguments +} + +const [KnownArguments, UnknownArguments] = MainParser.parse_known_args(Argv) +module.exports.Arguments = KnownArguments +module.exports.Unknown = UnknownArguments +module.exports.Parser = MainParser \ No newline at end of file diff --git a/TypeWriter/package.json b/TypeWriter/package.json index 73251d0..9824e0c 100644 --- a/TypeWriter/package.json +++ b/TypeWriter/package.json @@ -1,6 +1,6 @@ { "name": "typewriter", - "version": "6.0.0-beta.3", + "version": "6.1.0", "description": "A code archiver for lua and js", "author": "CoreByte", "license": "MIT", @@ -14,13 +14,11 @@ "fs-extra": "^11.1.0", "get-caller-file": "^2.0.5", "is-builtin-module": "^3.2.1", - "is-online": "^10.0.0", "klaw-sync": "^6.0.0", - "node-fetch": "^2.6.13", - "randomstring": "^1.2.3", + "node-fetch": "^2.7.0", + "p-all": "^3.0.0", "require-from-string": "^2.0.2", "require-main-filename": "^2.0.0", - "sync-fetch": "^0.4.2", "tar": "^6.1.13", "wasmoon": "^1.15.0" },