Giter Site home page Giter Site logo

rbxmk's Introduction

rbxmk logo

rbxmk is a command-line tool for manipulating Roblox files.

rbxmk is useful for development workflows that involve the combination of many separate files. If your project is organized as Lua files for scripting and model files for assets, rbxmk makes it simple to combine them into a final product, be it a game, plugin, model, module, and so on. rbxmk is also suitable for more simple actions, such as downloading models or publishing games.

Download

The current version of rbxmk is v0.9.1. The following builds are available for download:

Windows Mac Linux
Windows 64-bit macOS Linux 64-bit
Windows 32-bit Linux 32-bit

See the Release page for more information on the current version.

rbxmk is fully featured, but thorough testing of all features is still a work in progress. Please practice redundancy and use backups to reduce the risk of data loss. Be sure to report issues as you encounter them!

Usage

rbxmk is a command-line tool, and so requires a command-line interface to use.

rbxmk primarily uses Lua scripts to produce and retrieve data, transform it, and send it off to a variety of sources. The main subcommand is run, which executes a script:

echo 'print("Hello world!")' > hello-world.lua
rbxmk run hello-world.lua
# Hello world!

The Documentation page provides a complete reference on how rbxmk is used, as well as the API of the Lua environment provided by rbxmk.

Examples

The examples directory contains examples of rbxmk scripts.

Installation

In addition to prebuilt releases, rbxmk can be installed manually.

  1. Install Go
  2. Install Git
  3. Using a shell with Git (such as Git Bash), run the following command:
go install github.com/anaminus/rbxmk/rbxmk@latest

If you installed Go correctly, this will install the latest version of rbxmk to $GOPATH/bin, which will allow you run it directly from a shell.

A specific version of rbxmk may be installed by replacing latest with a version number (e.g. v0.9.1).

Development

To compile and install the bleeding-edge version, the best way is to clone the repository:

git clone https://github.com/anaminus/rbxmk
cd rbxmk/rbxmk
go install

More information is available in the INSTALL document.

An effort is made to ensure that the latest commit will at least compile. However, it is not guaranteed that everything will be in a production-ready state.

License

The source code for rbxmk is available under the MIT license.

rbxmk's People

Contributors

anaminus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rbxmk's Issues

Implement Region drill

Drills down into a slice of a script or string-like value.

Reference specifies nested words, indicating sections within the string.

  • A Specifies region A
  • A.B Specifies region B within region A

Input: Retrieves the content of the region.
Output: Replaces the region content with the input. Adding + to the end of the reference (e.g. A.B+) will cause the input to be appended to the end of the region instead.

Parsing regions

Syntax:

  • --#Region: Begins Region.
  • --#/Region: Ends Region.
  • --[[#Region]]: Begins Region inline.
  • --[[#/Region]]: Ends Region inline.
  • Region tags use Lua comment syntax.
  • Ending a region also ends all sub-regions.
  • Inapplicable end tags are ignored. An end tag that doesn't match a start tag opens and closes a region of size 0, placed before the tag so that appending works.

Region content:

  • Start of selection
    • Begins after the start tag, after skipping a sequence of newlines.
    • Inline tag: Begins directly after start tag (the last ]).
  • End of selection
    • Ends before the start of the end tag (the first -; newline is included in the selection).
    • Inline tag: Ends before the start of the end tag.

Implement schemes

Umbrella issue for future schemes to be implemented.

  • rbxasset
  • rbxassetid
  • studio

Do something about convertType

convertType is used when setting a property while an instance has a descriptor, to validate the type, and attempt to convert the underlying value to the property descriptor's value type. Most of the time, Value.Type() is matched, and the value is validated just fine. However, it also contains a bunch of hard-coded switch cases for converting all the various exprims to the expected type. It would be better if this were spread out as an option to each type's reflector.

rbxmk Usage

When I try to open the file, nothing happens. When I click it to much a temp output show's up, mind explaining how to install it?

Figure out errors

  • Get rid of errors that include (anonymous) for context. This is unnecessary noise.
  • Avoid ArgErrors in index/newindex.
  • Improve error messages when accessing unregistered items.
  • Errors seems to stack up significantly, repeatedly displaying the script location.

Setup automated releases

  • Use GitHub Actions.
  • Generate releases with executables when a new version tag is pushed.
  • Generate for the following systems:
    • darwin/amd64
    • linux/386
    • linux/amd64
    • windows/386
    • windows/amd64

help on understanding the code

Hi,

I stumbled upon your project while learning lua as a way to support plugin like extension to my go program. Mainly to define workflow or a way to describe custom behavior. your code is extremely well written and documented. I never used Roblox or any background in gaming, so couldn't really understand what are the inputs and what kind of modifications can be done through scripting . It would be great if you can provide a sample file and a quick walkthrough how you apply transform through lua file. Greatly appreciate your time on this.

Thanks,
bsr.

Running rbxmk causes panic for unresolved fragments

The executable panics every time I run it regardless of arguments passed.

I am running Windows 10 64-bit and trying to use the 0.7.1 executable. This does not happen when using the 0.6.2 executable.

Here is a text file containing what it prints out (I can post this in a reply, if you prefer): rbxmk-output.txt

Edit: I just built from source and this still persisted. Let me know if you need any information about my system.

Add options to HTTP functions

http.read(options: HTTPOptions): (value: any)
http.write(options: HTTPOptions, value: any)

HTTPOptions:

Field Type Notes
URL string
Format string
Desc RootDesc|false|nil Passed to format options.
Cookie Cookie?

Slice regions

Specify numbers indicating offsets to slice the string.

  • : Entire string.
  • n: From n to end.
  • :m From start to m.
  • n:m From n to m.
  • n and m are clamped.
  • Input: slices input string.
  • Output: specified region is replaced with input with a copy-like behavior.
  • Add to DrillRegion.
  • Section names cannot start with a number.
  • If a section name begins with number or :, it indicates a slice of that
    section.

Better support for reading models and saving to .rbxmx files

It seems like rbxmk doesn't currently do a great job at saving models to .rbxmx files. Attempting to import into Roblox Studio a .rbxmx generated by rbxmk will be unsuccessful.

I don't know if this is a known issue due to technical limitations, but here is how I am able to fix a corrupted .rbxmx file, even for somewhat complex models.

  1. For tags containing an asset URL, wrap the url inside a tag:
BEFORE:

<string name="PROPERTY">rbxassetid://ASSET_ID</string>

AFTER:

<string name="PROPERTY"><url>rbxassetid://ASSET_ID</url></string>
  1. Enter the appropriate value inside the ModelMeshData. Not sure how this is found programmatically, but I get it from a .rbxmx file that was directly exported from Roblox Studio.
BEFORE:

<SharedString name="ModelMeshData"></SharedString>

AFTER:

<SharedString name="ModelMeshData">**MODEL_MESH_DATA_STRING**</SharedString>
  1. PhysicalConfigData does not seem to contain a valid value. In the same manner as the previous fix for ModelMeshData, I get the tag value from a .rbxmx exported from Studio.
BEFORE:

<SharedString name="PhysicalConfigData"><![CDATA[**SOME_CDATA_STRING**]]></SharedString>

AFTER:

<SharedString name="PhysicalConfigData">**PHYSICAL_CONFIG_DATA_STRING**</SharedString>
  1. Finally, at the bottom of the .rbxmx file, add a new set of tags to reference to MODEL_MESH_DATA_STRING, PHYSICAL_CONFIG_DATA_STRING and SOME_CDATA_STRING.
<SharedStrings>	
	<SharedString md5="**MODEL_MESH_DATA_STRING**"></SharedString>	
	<SharedString md5="**PHYSICAL_CONFIG_DATA_STRING**">**SOME_CDATA_STRING**</SharedString>	
</SharedStrings>

Support appending in property drill

When specifying a property as an output, adding + to the end of the reference will cause the input to append instead of replace.

Not yet sure if this still makes sense after recent refactors.

Integrate command flags

  • Move main body to run sub-command.
  • Paves way for other sub-commands, like a server, or a declarative CLI.
  • Cobra is feature-complete, but really freaking big. Would rather use flag if possible.

Refactor libraries

Use of the standard library (string, math, table) is becoming more attractive. Instead of the current stripped-down environment, refactoring it to be more standardized may be better off. It will be easier for developers used to a regular Lua environment.

  • Move current functions to rbxmk library. The table-argument structure can remain.
  • Add basic standard libraries: base, string, math, table.
  • Merge functions like print and error with their similar standard counterparts.

Lua preprocessors

The region filter is being abused for preprocessor-like behavior. More preprocessor behavior is desired. Why not add a full preprocessor filter?

  • Uses Lua as the preprocessor.
  • Based off of luapp.lua.
  • Preprocessors are enclosed within comments so that Lua source remains valid.
  • Conflicts with region syntax.
    • Refactor region filter so that it only looks for given regions, ignoring unspecified ones.

Implement Instance.IsA and related methods

  • Was skipped because descriptors weren't implemented yet.
  • When the instance has a descriptor, methods work as normal.
  • When the instance has no descriptor, methods just return the sensible negative (false, nil).
  • The reflected methods could also return the negative, or could instead throw errors.
  • Implement the following methods:
    • IsA
    • FindFirstAncestorWhichIsA
    • FindFirstChildWhichIsA

Skip userdata caching for exprim types

When the following code is called:

types.BinaryString("")

The following error is thrown:

hash of unhashable type types.BinaryString

This is because the underlying type is []byte. Caching is used to convert the value to an exprim userdata, which uses a map to store the value. []byte cannot be compared, so it cannot be used as a map key, causing a panic.

Exprims are a niche feature, and exprim values are not meant to be used anywhere except next to properties. So, instead of constructing an exprim userdata from the World, create a new one directly. This means that exprims cannot be properly used as table keys, but that's fine. Once again, they are a niche feature.

rbx formats respect descriptors when encoding and decoding

  • Add "Desc" format option to each format that passes a RootDesc.
  • If Desc is unspecified, use rbxmk.Global.Desc.

The XML formats use a normalizeProps function, which utilizes the global descriptor to convert types, but this is only used as a filter to convert between int and BrickColor (Roblox encodes BrickColor as int for whatever reason).

This system can be refined to be more generalized. Rather than traversing all propeties at once, the descriptor can be passed to encode/decodeValue to be consulted as needed, during the conversion between types.Value and rbxfile.Value.

An exception for BrickColors in rbxlx will still need to be maintained. On the other hand, this is not actually necessary in order for BrickColors to be decoded (Roblox ignores type names entirely, relying only on the class/property names), so it may be easier to just allow rbxmk to encode BrickColors as they are. Less canonical, but more "correct".

Support directory-like nodes

If I say input{"project/folder/subfolder/"}, the input should contain all the parsable files in the specified folder. If I say output{"project/folder/subfolder/"}, inputs mapped to the output should be mapped as files to the folder.

Problem: Inputs are read and parsed immediately; we would have to read the entire contents of the folder into memory. Often, only one file is modified at a time while mapping, so constantly reading files in and writing files out is greatly inefficient.
Solution?: Sources are abstracted, with their implementation hidden. The implementation of directory sources only read files when necessary.

Problem: Sources themselves don't have enough context to be mapped to an unspecified file.
Solution: Sources have a FileName field (this was already added), which can be used to map the Source to a file.

Implement Roblox environment additions

Roblox has several additions to its Lua environment. Several come from later versions of Lua, while others are homemade.

  • math.clamp: Clamping function.
  • math.log(x, base): Log with optional base.
  • math.round: Round to nearest integer.
  • math.sign: Return sign of number.
  • string.split: Split by separator.
  • table.clear: Efficiently empty a table.
  • table.create: Efficiently create a new table.
  • table.find: Find the index of an entry.
  • table.move: Efficiently copy entries.
  • table.pack: Pack arguments.
  • table.unpack: Unpack arguments.

Document package APIs

Not that it's likely these packages be used outside of this project, it's worth documenting them for future me, and future contributors.

Also, don't be lazy, you bum.

Make Instance and DataModel different types

  • DataModel type can embed instance.
  • Reflector will have to be repeated, but will have modifications.
  • Would be able to put DataModel under rbxmk library rather than roblox library.
  • Separate symbol implementation that only includes Desc and RawDesc.

Not really sure. Revisit after the codebase matures a bit more.

Implement Minify filter

Minify Lua scripts for whatever reason.

node = filter{"minify", node}

How to implement?

  • Call lua-minify
    • Execute; requires Lua to be installed
    • Run with go-lua gopher-lua; must ensure script is compatible
  • Port lua-minify to Golang
    • Potentially time-consuming

Validate property types

Add Type field to Property. If the type is not invalid, then it is the expected type of the property's value. When creating a Property, use API to set the Type, if available.

Add print/sprintf functions

It may be useful for scripts to output information.

print{...}

Prints each argument to stdout. Arguments are converted to strings, then joined with a space as a separator, and finally a newline.

string = sprintf{format, ...}

Formats a string using the same rules as string.format.

printf{format, ...}

Uses sprintf to format a string and prints the result to stdout.

clipboard.write cannot encode non-variant types

Certain formats specify EncodeTypes, allowing them to encode types that don't work with Variant (e.g. Objects, DescActions). Writing to the clipboard involves a number of format selectors, but only one value, which is pulled as a Variant. This causes the clipboard to fail on these non-variant types. In order to properly handle this, pulling the value must delayed so that each format selector can interpret the value in its own way.

Currently, the clipboard implementation is independent of the reflection layer (ClipboardSource) to provide a generic API usable by anything. An implementation that retains ClipboardSource would involve extracting the Lua stack to a []lua.LValue and passing it to ClipboardSource.Write.

The easier option would be to remove ClipboardSource entirely, and move the implementation back into the clipboard library functions. So far, I don't really see a use for having a reflection-independent implementation anyway. Notably, ClipboardSource isn't used anywhere else except the clipboard library. It also has a dependency on World, but only to get registered Formats and the world's Global for passing to Formats.

Support API dumps

rbxfile can use API dumps to improve how it encodes and decodes files. rbxmk should also utilize this.

  • api = loadapi{filename}: Returns an API object that can be passed to other functions.
  • defaultapi{api}: Sets the default API object that is used by other functions.

Functions can receive an API object via the api named-argument.

  • input
  • output
  • filter: Where applicable

Colon character within a URL is detected as a drill separator

In the http scheme, instances of a : character within the URL are detected as the drill separator, including URL-escaped instances. This will truncate the URL to that point, which may not be desirable. This should be fixed so that URL-escaped colons will not be detected as such, though normal colons still will be.

An exception to this is when a port is specified, which is detected correctly.

http://www.example.com:80/path/to/file:etc
                                      ^ first drill separator

This problem occurs because The url package is not flexible enough when it comes to raw, unescaped URLs. A modified version or new implementation of the package will be necessary.

Implement generate scheme

Generates data on the fly.

Scheme

Input only: Generating an output doesn't really make sense.

generate://<type>

Types:

  • Instance
  • Property
  • Value

The reference specifies the content of the generated data.

Instance

The reference has the following syntax:

root       = <class> { `;` <class> } [ `;` ]
class      = <name> `{` [ <properties> ] `}`
properties = <property> { `;` <property> } [ `;` ]
property   = <name> <type> `:` <value>
name       = <string>
type       = <string>
value      = <component> { `,` <component> } [ `,` ]
component  = <bool> | <number> | <string>
bool       = `true` | `false`
number     = ! a number !
string     = ! a string, possibly quoted with " or ', escaped with \ !

Problem: How to implement <value>
Solution: Use rbxfile/declare: declare can fill in any value using only bools, numbers, and strings.

Property

The reference uses the <properties> syntax.

Value

The reference uses the <value> syntax.

Release procedure

  1. Clean up the imperative section of the change log.
    • Reorder changes based on interest.
    • Linkify things that can be.
    • Note that, on release, instances of imperative within the section (including links) will be replaced by the released version tag (e.g. v0.6.2).
  2. Check for new versions of actions.
  3. Ensure that target Go version is present in actions/go-versions.
    • Otherwise, force version by updating GOVERSION.
  4. Check that the GOVERSION environment variable in release.yml and test.yml supports the version in go.mod.
  5. Run the release tool.
    • In the repo root, run go run _tools/version.go +minor, or whatever needed to increment version:
      • +major increments the major version (vX.0.0).
      • +minor increments the minor version (v0.X.0).
      • +patch increments the patch version (v0.0.X).
  6. Verify that the release commit is correct.
  7. Push the release commit to trigger the test workflow.
  8. Wait for the test workflow to finish successfully.
    • Check that "Set up Go" job used target Go version.
  9. Push the release tag to trigger the release workflow.
  10. Wait for the release workflow to finish successfully.
  11. Verify and publish the generated release draft.
  12. Update DevForum post.
  13. Have a proverbial beer.

Support Rojo project files

With a few additions, an rbxmk script can be written to parse a Rojo project and compile the result, and vice-versa. This could be used to integrate more easily with existing projects, and aid transitions between Rojo and rbxmk.

This could be implemented as a script, but could also be a sub-command for better UX.

Dependencies

  • #40: Add generic JSON format
  • #33: Integrate command flags (if command)

References

TODO

  • Explore implementation details

Fork gopher-lua

  • GL prepends some errors with line information, which gets unwieldy when errors stack up. This information should stay in the error's stack trace.
  • "attempt to call a non-function object" is not informative enough.
  • GL's implementation of string.format piggybacks off of fmt.Sprintf.

It can either be kept in a separate repo, with a focus on improving compatibility with Lua and merging changes upstream. Or it can be vendored for rbxmk-specific enhancements.

Refactor Source system into Data system

Sources are inflexible, and usually use only one type of data they are able to contain.

Data is an empty interface which contains arbitrary data. Most of the heavy lifting is done by Format; a format decodes into a specific type, it defines drills which operate on the type, it knows how to merge other types into the type, and it knows how to encode the type.
The format package exposes these types for the benefit of other packages (i.e. filter) that may operate on them.

resolves #2, obsoletes #4

Invalid reference in Model.PrimaryPart causes crash

Using the following script rbxmk produces a .rbxm file that crashes roblox studio.
Through some debugging I've determined that it's related to the PrimaryPart property on Model1

PoC Script:

local Datamodel = fs.read("Bruh.rbxm", "rbxm") --A roblox generated rbxm file containing a "Model" Instance
Datamodel[sym.RawDesc] = rbxmk.globalDesc --Use roblox's default descriptors.

local Model0 = Instance.new("Model") --Create a new model in rbxmk
local Model1 = Datamodel:GetChildren()[1]
Model1.Parent = Model0
Model0.Parent = Datamodel

fs.write("PoC.rbxm", Datamodel, "rbxm") --This written file will crash studio on load

Command line in use:
rbxmk run -desc-latest bruh.lua

Reproduction Instructions:

  1. Go into roblox studio and create a model instance
  2. Save it as a file in the roblox binary format
  3. Name it to bruh.rbxm in the working directory or alter the script above to load it in
  4. Take the outputted PoC.rbxm file and load it into roblox studio
  5. It will instantly close due to a crash.

Interestingly enough doing this works around the issue:

local Datamodel = fs.read("Bruh.rbxm", "rbxm") --A roblox generated rbxm file containing a "Model" Instance
Datamodel[sym.RawDesc] = rbxmk.globalDesc --Use roblox's default descriptors.

local wtf = Instance.new("BasePart") --This ends up not getting deserialized, but it fixes the issue?
wtf.Parent = Datamodel

local Model0 = Instance.new("Model") --Create a new model in rbxmk
local Model1 = Datamodel:GetChildren()[1]
Model1.Parent = Model0
Model0.Parent = Datamodel

fs.write("PoC.rbxm", Datamodel, "rbxm") --This written file will crash studio on load

Add source profiles

A Profile is an interface that tells whether a Source meets certain criteria. For example, a ScriptProfile would indicate whether a Source can be parsed as a script.

Use-case:
LuaFormat has a lot of places where it checks if the Source is a script instance or a string-like value. Having a profile will consolidate this repetitive code.
Several filters will operate only on script-like data, so this same profile could be used there as well.

Implement filters

Umbrella issue for future filters to be implemented.

  • tostring: Convert any string-like type to any other string-like type, scripts included.

Implement API for DescActions

The main problem is how to deal with the Fields member, which maps a string to an arbitrary value. Other members are straight-forward.

Change API for setting DataModel metadata

  • Using properties feels kinda not great. Some names are blocked by other members, etc.
  • Metadata/SetMetadata methods?
  • DataModel[sym.Metadata]: Dictionary<string>: User has to get/set metadata all at once, but who cares?
    • I like this one.

Mark endpoints as readonly

readonly{ endpoint }

Specifies that endpoint is readonly. rbxmk will not be able to write to the endpoint for the remaining duration of the program. This is used as a safeguard to prevent unintended overwriting.

Schemes must implement a function that compares two endpoint strings and returns whether or not they are equal. Marked endpoints are stored in Options, per scheme.

API for zip files

rbxmk should be able to read and write zip files. Go has a nice API for it, the issue is how the Lua API should look:

  • Should be implemented as a format: zips can come from any source.
  • The decoded value is non-trivial. How should this be implemented?
  • Probably some sort of "Zip" userdata, with methods.
  • Should be an interface: Most sources will return a Zip that reads from memory, but file can return a Zip that reads directly from the file.
  • This doesn't interact well with the current implementation of formats, which want to read/write an entire []byte all at once.

Support MoonScript

Some developers like to use MoonScript, so why not?

MoonScript is parsed with the .moon format. Could be parsed as a script of a particular type, as with LuaFormat (e.g. file.modulescript.moon).

How to implement?

  • moonscript appears to be written in Lua.
    • Execute; requires Lua to be installed Has standalone executable.
    • Run with go-lua; must ensure script is compatible Has 3rd party dependencies
  • Port moonscript to Golang
    • Certainly time-consuming

Add generic JSON format

Match Roblox's implementation.

References

Implementation details

Decoding

Should be okay to use encoding/json.

  • nil => LNil
  • bool => LBool
  • float64 => LNumber
  • string => LString
  • []interface{} => LTable (array)
  • map[string]interface{} => LTable (map)

Encoding

Should be okay to use encoding/json.

  • Any other type not listed => nil
  • LNil => nil
  • LBool => bool
  • LNumber => float64
    • Infinity, NaN => nil

LString

The following characters are escaped:

Character Code Escape
Backspace 0x08 \b
Horzontal tab 0x09 \t
Line feed 0x0A \n
Form feed 0x0C \f
Carriage Return 0x0D \r
Double quote 0x22 \"
Backslash 0x5C \\

Control characters (0x00 - 0x1F) not in this list are escaped as \u00XX, where XX is the character code in hexadecimal.

Other unicode characters are encoded as-is. A malformed unicode character within any encoded string causes the following error to be thrown:

Can't convert to JSON

LTable

A table is encoded as either a JSON array or a JSON object, which depends on the content. A table is interpreted as an array if it is empty, or if it contains the integer 1 as a key. Otherwise, it is treated as an object.

Tables are encoded recursively. If a table that is being or has already been encoded is traversed, the following error is thrown:

tables cannot be cyclic

Array

An array encodes integer keys from 1 to the length of the table. Other keys are ignored, and are not traversed.

Object

An object encodes certain types of keys to JSON strings. Keys are encoded in the same way as string values.

If a key is a boolean or thread, the following error is thrown:

Invalid table key type used

If a key is a number, it is converted to a 32-bit signed integer, then converted to a string.

Other types are converted to a string in the following format:

<TYPE> (TOSTRING)

Where TYPE is the Roblox type of the value, and TOSTRING is the value converted to a string. Note that types of table and function are formatted in title-case (Table, Function). Additionally, an unknown userdata type (newproxy()) is formatted as void.

This exact implementation is convoluted and unnecessary. Instead, non-string keys will be ignored and not traversed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.