flinedev / anylint Goto Github PK
View Code? Open in Web Editor NEWLint anything by combining the power of scripts & regular expressions.
License: MIT License
Lint anything by combining the power of scripts & regular expressions.
License: MIT License
Sometimes a single regex isn't enough to catch and autocorrect all cases. For example, consider we want to add _
after each third number in long number literals, so we want to autocorrect 50000000000
to 50_000_000_000
. While a pattern can be written like (\d+)(\d{3})
with a replacement of $1_$2
to fix the 3 last numbers, new checks need to be written to autocorrect other lengths.
Add an option to the checkFileContents
method to repeat the check if autocorrections were applied until none are applied anymore. Could be named like repeatIfAutocorrected
.
The exact example from the problem statement with repeatIfAutocorrected
set to true
.
When I autocorrect a file path to a new path, the new path is created and the file is moved to it (unless the file already exists).
The parent folders are missing and I'm getting this error:
Fatal error: Error raised at top level: Error Domain=NSCocoaErrorDomain Code=4 "“ExampleInstrumentedTest.kt” couldn’t be moved to “api_client” because either the former doesn’t exist, or the folder containing the latter doesn’t exist." UserInfo={NSSourceFilePathErrorKey=/Users/Cihat/Code/Papershift/API-Client-Android/api_client/src/androidTest/java/com/papershift/api_client/ExampleInstrumentedTest.kt, NSUserStringVariant=(
Move
), NSDestinationFilePath=/Users/Cihat/Code/Papershift/API-Client-Android/api_client/src/androidTest/kotlin/com/papershift/api_client/ExampleInstrumentedTest.kt, NSFilePath=/Users/Cihat/Code/Papershift/API-Client-Android/api_client/src/androidTest/java/com/papershift/api_client/ExampleInstrumentedTest.kt, NSUnderlyingError=0x7fabae43a8b0 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}: file /AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1103.8.25.8/swift/stdlib/public/core/ErrorType.swift, line 200
14:38:01.942: ❌ Linting failed using config file at /Users/Cihat/Code/Papershift/API-Client-Android/lint.swift.
Using this check:
// MARK: KotlinSourcePath
try Lint.checkFilePaths(
checkInfo: "KotlinSourcePath: All Kotlin source files should be placed under .",
regex: #"^(.*/src/.*)(/java/)(.*\.kt)$"#,
matchingExamples: ["app/src/main/java/com/android/BaseViewModel.kt", "app/src/debug/java/uk/co/hogwarts/TriWizardTournament.kt"],
nonMatchingExamples: ["app/src/main/kotlin/com/android/BaseViewModel.kt", "app/src/debug/kotlin/uk/co/hogwarts/TriWizardTournament.kt", "app/generated/java/Script.kt"],
autoCorrectReplacement: "$1/kotlin/$3",
autoCorrectExamples: [
["before": "app/src/main/java/com/android/BaseViewModel.kt", "after": "app/src/main/kotlin/com/android/BaseViewModel.kt"],
["before": "app/src/debug/java/uk/co/hogwarts/TriWizardTournament.kt", "after": "app/src/debug/kotlin/uk/co/hogwarts/TriWizardTournament.kt"],
]
)
Such a converter could transform this:
class_name_suffix_collection_view_controller:
included: ".*\.swift"
regex: 'class +\w+(?<!CollectionViewController) *(?:<[^>]+>)? *: +\w+CollectionViewController'
name: "Class Name Suffix View Controller"
message: "All `CollectionViewController` subclasses should end on `CollectionViewController`."
severity: warning
to something like this:
// MARK: ClassNameSuffixCollectionViewController
try Lint.checkFileContents(
checkInfo: "ClassNameSuffixCollectionViewController: All `CollectionViewController` subclasses should end on `CollectionViewController`.",
regex: #"class +\w+(?<!CollectionViewController) *(?:<[^>]+>)? *: +\w+CollectionViewController"#,
matchingExamples: [], // TODO: [2020-04-21] not yet implemented
nonMatchingExamples: [], // TODO: [2020-04-21] not yet implemented
includeFilters: #".*\.swift"#
)
Whenever I specify the comment // AnyLint.skipHere: RuleName
somewhere, I expect the rule violations to be ignored.
When using a customCheck
custom Swift code to create a violation, the ignore does not work.
The applied autocorrection report in the command line should give some context, even when the regex only maches only a very small portion of a line.
Only the matched regex is shown in output.
Like // swiftlint:disable <rule_name>
there should be a way to disable rules for specific lines or files. The design might look something like this:
// #!anylint:ignore <RuleName>!#
Sometimes it is easier to create a regex that doesn't only match when there's an error, but it matches given structures to autocorrect them. For example, consider this check:
// MARK: ObjCFunctionWhitespace
try Lint.checkFileContents(
checkInfo: "ObjCFunctionWhitespace@info: Whitespaces on ObjC functions should be of structure: `- (type)function:(Type *)param {`",
regex: #"^([\+\-]) *\( *([^)]+) *\) *([^\n\{]+[^ \n])\s*\{\s*\n( *\S+)"#,
matchingExamples: ["- (void)viewDidLoad\n{\n blubb", "-(void)viewDidLoad {\n blubb"],
includeFilters: [objectiveCFiles],
autoCorrectReplacement: "$1 ($2)$3 {\n$4",
autoCorrectExamples: [
["before": "- (void)viewDidLoad\n{\n blubb", "after": "- (void)viewDidLoad {\n blubb"],
["before": "-(void)viewDidLoad {\n blubb", "after": "- (void)viewDidLoad {\n blubb"],
]
)
This was set to level info
to not report any warnings or errors, but in some cases it does have warnings or errors, in others it doesn't. This could be auto-detected by checking if the replacement of the autocorrection is actually the same or not, then there would be no need for @info
level and no violations such as this would be reported:
17:22:01.956: ℹ️ > 1140. App/Sources/AppDelegate.m:1574:1:
17:22:01.956: ℹ️ Autocorrection applied (before >>> after):
17:22:01.956: ℹ️ > ✗ -␣(void)applicationWillResignActive:(UIApplication␣*)application␣{\n␣␣␣␣//
17:22:01.956: ℹ️ >>>
17:22:01.956: ℹ️ > ✓ -␣(void)applicationWillResignActive:(UIApplication␣*)application␣{\n␣␣␣␣//
If there is an autoCorrectReplacement
specified on a rule, we should remove the violation from the results as there seems to be no violation. This enables some easier regex specifications and doesn't require to split them up e.g. when they match from beginning or end. The regex is then not only used for violations, but more for the entire structure that should be autocorrected. It is something like an autocorrect-only check.
See above.
Currently when specifying AutoCorrection examples, a custom type is needed. For everything else, literals can be used. Thus it might make sense to make the AutoCorrection
type also intitializable by just specifying ["before": "...", "after": "..."]
As it's in the name of AnyLint, it should be able to lint anything. This can also be understood in a way that AnyLint can start and orchestrate any kind of linter. This will only work though if those other linters output can be understood by AnyLint and they can somehow communicate / interpret each others results.
Add a standardized JSON-based violation & statistics output format which other tools outputs can be easily converted to. Then add native support for some common linters like SwiftLint, SwiftFormat and ktlint to make AnyLint summarize their results and fail at the specified levels.
A user might want to setup some custom lint rules via AnyLint and additionally specify that SwiftLint should be run. This could be done via a command line script that is specified in AnyLint and somehow another command that is provided by AnyLint which can convert the output to the AnyLint standardized format and therefore the summary and failing status of anylint
would also include the results of SwiftLint.
To show warnings & errors directly in Xcode using a build script, we should introduce an option to output the warnings in the format compatible to show errors right within Xcode. We should also add a section (probably expandable) with a copy & paste build script for Xcode integration.
I use AnyLint with Xcode 14.3 on MacOS Ventura.
I use with an older Objective C project.
I setup AnyLint a few months ago.
However, I've sstarted getting this error
unable to load standard library for target 'x86_64-apple-macosx12.0'
I've traced this back to my AnyLint build phase.
Currently it's error
which makes builds fail when running via Xcode build script. And there's not even an option like --lenient
to downgrade errors to warnings.
Just default to the warning level instead of error like now.
Currently, when running the Swift script even with no changes to the Script file, the whole Swift compiler is started fresh leading to the script taking a few seconds and letting one wait longer than needed.
Instead of simply running the Script file via swift-sh, wrap it in a command line container and build an executable file. Then reuse that executable file as long as the Script file contents are identical, e.g. by saving its SHA hash. This is like a caching mechanism.
Setting up anylint in pre-commit hooks should be as easy as possible. Thus, adding a command like anylint --hook
might make sense. The command should output the changes made, so users who are not aware of hos git hooks work can just open the pre-commit hook file and learn about it, with the possibility to add more commands like swiftlint
if needed.
Currently, installing and running AnyLint on the CI takes too much time.
This step could potentially be officially documented as a way to set AnyLint up on CI:
https://github.com/Cyberbeni/install-swift-tool
I am was working on this autocorrect for long single line function headers so it automatically corrects them into multiple lines but since it was too complex i broke it into two separate autocorrects. First to break paramters into a singular line away from the func ( ) -> x surounding it and a second one that cycles over the match and break the last parameters onto a separate line every cycle. However eventhough it matches correctly as soon as I apply autocorrection to the second rule nothing ever changes. Please help me I am very confused because event if I switch out the replacement for litterally any matching group it ignored the autocorrect
`let longMultilineFunctionDefMatchingExample = "static func defaultAlert(verylongStringToPutToMultipleLineThatExceeds120charactersForSureSinceItIsSoVeryLongOhMyGod: Sting) {\n}"
let longMultilineFunctionDefNonMatchingExample1 = "\tstatic func defaultAlert(title: String, title: String, title: String, message: String) {\n}"
let longMultilineFunctionDefBeforeAutocorrect = "\tstatic func defaultAlert(verylongStringToPutToMultipleLineThatExceeds120charactersForSureSinceItIsSoVeryLongOhMyGod: String){\n}"
let longMultilineFunctionDefAfterAutocorrect = "\tstatic func defaultAlert(\n\t\tverylongStringToPutToMultipleLineThatExceeds120charactersForSureSinceItIsSoVeryLongOhMyGod: String\n\t){\n}"
try Lint.checkFileContents(
checkInfo: "Long multiparameter function: Parameters of functions above 120 character should be split into multiple lines",
regex: #"^(?=.{121,})(?[ \t])(?
try Lint.checkFileContents(
checkInfo: "Multiline function definition: Parameters of functions above 120 character should be split into multiple lines",
regex: #"^(?<SPACING>[ \t]*)(?<HEADER>(?:[a-zA-Z@ ]*)func.*\()\n^[ \t]*(?<PARAMETERSTOKEEP>(?:.*\: .*,(?!\n))+) (?<PARAMETERSTOFORMAT>(?:\s*.*\: .*,{0,1}(?=\n))*)\n^(?<HEADEND>[ \t]*\).*(?:->){0,1}.*\{(?=\n))$"#,
matchingExamples: ["func makePendingEventsSections(\nfor pendingEvents: [Event], photos: [RolePhoto], statusOverviews: [PresenceOverviewResponse]\n) -> [Section] {\n}"],
nonMatchingExamples: ["\tstatic func defaultAlert(\ntitle: String) {"],
includeFilters: [#".*\.swift"#],
excludeFilters: excludeFilters,
autoCorrectReplacement: "$SPACING$HEADER\n$SPACING\t$PARAMETERSTOKEEP\n$SPACING\t$PARAMETERSTOFORMAT\n$SPACING$HEADEND",
autoCorrectExamples: [
["before": "static func defaultAlert(\n\ttitle: String, title: String, title: String, message: String, hi hi: no, in controller: (UIViewController\n) -> Void) {\n}",
"after": "static func defaultAlert(\n\ttitle: String, title: String, title: String, message: String, hi hi: no,\n\tin controller: (UIViewController\n) -> Void) {\n}"]
],
repeatIfAutoCorrected: true
)`
single line function parameters break into separate lines each
first rule works having all parameters on one line but second one is ignored (eventhough it matches)
class func from( html: String?, font: (regular: UIFont, bold: UIFont), color: UIColor, documentAttributes: AutoreleasingUnsafeMutablePointer<NSDictionary?>? = nil ) -> NSAttributedString? { return from(html: html, font: font, size: font.regular.pointSize, color: color, documentAttributes: documentAttributes) }
When running the -init
command, I expect that the resulting lint.swift
file points to the correct path of swift-sh. The correct path should be automatically detected.
Currently, it's pointing to the /usr
directory, but in latest Homebrew it's placed under /opt
.
anylint --init blank
I’d like into include my development pods in my objective c project in the include filte filter.
The pods don’t have their own project file.
I can’t edit the files within the pods folder as they are locked, in fact I don’t want to include my pod folder.
So I’d like to use ../../PrivatePods/MyPod etc
However, nothing works for me.
Also would you accept a PR to include an example section, so I could add a link to my objective c rules, I’d could create my own repo to accept changes to my rules file ?
Thanks,
Such a great package 👍
Jules
Sometimes one needs a regular expression with exceptions. For example consider a regex that matches all literal numbers with 4 digits or more and reports a violation because no _
is used as a separator: (\d+)(\d{3})
. This is great, but actually we want to exclude years from this, as 1981
is more readable than 1_981
.
Add an option to exclude matches matching a given regex, for example by adding a new excludeMatchesMatching
regex to checkFileContents
.
With the regex
being (\d+)(\d{3})
, one could provide an excludeMatchesMatching
of (1[4-9]|20)\d{2}
to skip violations for numbers between 1400 & 2099 – this solves the problem.
Currently, when there's a Regex or Swift custom rule defined that takes significantly longer to execute its checks, users have to wait without knowing why.
Report performance of every custom rule somewhere and highlight outliers specifically to inform users about slow ones.
It's not as easy as it could be to setup AnyLint at the moment for GitHub Actions. Also, the results don't show up in GitHub changes as comments directly. Could also just be integrated with https://github.com/marketplace/actions/lint-action.
There should be a ready GitHub action similar to this action for SwiftLint for quick usage.
Currently the only possible way to setup simple Regex-based lint rules is to write Swift code. While this allows a maximum of flexibility, it has drawbacks:
Add a YAML-based rule configuration option where of course only the checkFileContents
and checkFilePaths
kind of checks are supported. It should be extensible though to include other YAML or even Swift configuration files where AnyLint would automatically include everything the right way to check all that's needed.
Currently, when doing changes in multi-line matches, the differences are not always clear. To make them more apparent, a diff could be used.
At the moment, we have 1 static template within the project, but instead we could build a template loading system, where different template portions could be combined to build a good starting template with a single command.
Also, a centralized separate repo might make sense to collect those templates. Similar to "rules" in other linters, but not included into AnyLint, instead loadable from anywhere with special treatment for the official community repo.
For a given comment like this:
// TODO: [cg_2016-05-28] find a better way to implement this
The rule could report a warning if the given TODO is between 3-6 months ago and an error if it's more than 6 months ago. That could be achieved with regexes, file reading, but with custom logic using the current date and transforming the date, which shows the strengths of AnyLint very well.
At the moment, getting a good overview of all the rules within a config file is hard. Thereforew, it would be great if a different and more useful kind of representation of the rules could be rendered, like a Markdown, JSON or HTML file. Consider for example Markdown, then this file:
#!/usr/local/bin/swift-sh
import AnyLint // @Flinesoft ~> 0.4.0
try Lint.logSummaryAndExit(arguments: CommandLine.arguments) {
// MARK: - Variables
let swiftSourceFiles: Regex = #"^App/Sources/.*\.swift$"#
let swiftTestFiles: Regex = #"^Tests/Sources/.*\.swift$"#
let swiftUITestFiles: Regex = #"^UITests/Sources/.*\.swift$"#
let allSwiftFiles: [Regex] = [swiftSourceFiles, swiftTestFiles, swiftUITestFiles]
// MARK: - Checks
// MARK: HandySwiftDelayTimeInterval
try Lint.checkFileContents(
checkInfo: "HandySwiftDelayTimeInterval: Use one of the HandySwift TimeInterval extension methods like `.milliseconds(500)` instead.",
regex: #"(\sdelay\(by:\s*)([\.\d]+)([,\)])"#,
matchingExamples: [" delay(by: 15) {", " delay(by: 0.5, qosClass: .background) {", " delay(by: .5, qosClass: .utility) {"],
nonMatchingExamples: [" delay(by: .seconds(15.0)) {", " delay(by: .milliseconds(500), qosClass: .background) {", " updelay(by: 15.0)"],
includeFilters: allSwiftFiles,
autoCorrectReplacement: #"$1.seconds($2)$3"#,
autoCorrectExamples: [
["before": " delay(by: 15) {", "after": " delay(by: .seconds(15)) {"],
["before": " delay(by: 0.5, qosClass: .background) {", "after": " delay(by: .seconds(0.5), qosClass: .background) {"],
["before": " delay(by: .1, qosClass: .utility) {", "after": " delay(by: .seconds(.1), qosClass: .background) {"],
]
)
}
Might be rendered to this Markdown:
# lint.swift
The following checks are included in this file:
## Checks
### HandySwiftDelayTimeInterval
Use one of the HandySwift TimeInterval extension methods like `.milliseconds(500)` instead.
<details>
<summary>Matching Examples</summary>
``
delay(by: 15) {
``
``
delay(by: 0.5, qosClass: .background) {
``
``
delay(by: .5, qosClass: .utility) {
``
</details>
<details>
<summary>Non-matching Examples</summary>
``
delay(by: .seconds(15.0)) {
``
``
delay(by: .milliseconds(500), qosClass: .background) {
``
``
updelay(by: 15.0)
``
</details>
<details>
<summary>Auto-correct Examples</summary>
Before:
``
delay(by: 15) {
``
After:
``
delay(by: .seconds(15)) {
``
---
Before:
``
delay(by: 0.5, qosClass: .background) {
``
After:
``
delay(by: .seconds(0.5), qosClass: .background) {
``
---
Before:
``
delay(by: .1, qosClass: .utility) {
``
After:
``
delay(by: .seconds(.1), qosClass: .utility) {
``
</details>
Which would be rendered on sites like GitHub to this:
The following checks are included in this file:
Use one of the HandySwift TimeInterval extension methods like .milliseconds(500)
instead.
delay(by: 15) {
delay(by: 0.5, qosClass: .background) {
delay(by: .5, qosClass: .utility) {
delay(by: .seconds(15.0)) {
delay(by: .milliseconds(500), qosClass: .background) {
updelay(by: 15.0)
Before:
delay(by: 15) {
After:
delay(by: .seconds(15)) {
Before:
delay(by: 0.5, qosClass: .background) {
After:
delay(by: .seconds(0.5), qosClass: .background) {
Before:
delay(by: .1, qosClass: .utility) {
After:
delay(by: .seconds(.1), qosClass: .utility) {
Not to fail when building even on Linux servers.
Fails with this error message sometimes:
+ source /root/.bash_profile
++ export SWIFTENV_ROOT=/root/.swiftenv
++ SWIFTENV_ROOT=/root/.swiftenv
++ export PATH=/root/.swiftenv/bin:/root/.bitrise/tools:/bitrise/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/opt/android-sdk-linux/tools:/opt/android-sdk-linux/tools/bin:/opt/android-sdk-linux/platform-tools:/opt/android-ndk
++ PATH=/root/.swiftenv/bin:/root/.bitrise/tools:/bitrise/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/opt/android-sdk-linux/tools:/opt/android-sdk-linux/tools/bin:/opt/android-sdk-linux/platform-tools:/opt/android-ndk
+++ swiftenv init -
++ eval 'export PATH="/root/.swiftenv/shims:${PATH}"
command swiftenv rehash 2>/dev/null
source '\''/root/.swiftenv/libexec/../libexec/../completions/swiftenv.bash'\'''
+++ export PATH=/root/.swiftenv/shims:/root/.swiftenv/bin:/root/.bitrise/tools:/bitrise/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/opt/android-sdk-linux/tools:/opt/android-sdk-linux/tools/bin:/opt/android-sdk-linux/platform-tools:/opt/android-ndk
+++ PATH=/root/.swiftenv/shims:/root/.swiftenv/bin:/root/.bitrise/tools:/bitrise/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/opt/android-sdk-linux/tools:/opt/android-sdk-linux/tools/bin:/opt/android-sdk-linux/platform-tools:/opt/android-ndk
+++ command swiftenv rehash
+++ source /root/.swiftenv/libexec/../libexec/../completions/swiftenv.bash
++++ complete -F _swiftenv swiftenv
+ AnyLint/.build/release/anylint
Fatal error: Duplicate keys of type 'SearchOptions' were found in a Dictionary.
This usually means either that the type violates Hashable's requirements, or
that members of such a dictionary were mutated after insertion.
Current stack trace:
0 libswiftCore.so 0x00007f3b7d08c490 swift_reportError + 50
1 libswiftCore.so 0x00007f3b7d0fdcd0 _swift_stdlib_reportFatalError + 69
2 libswiftCore.so 0x00007f3b7ce138ac <unavailable> + 1386668
3 libswiftCore.so 0x00007f3b7ce13c00 _assertionFailure(_:_:flags:) + 501
4 libswiftCore.so 0x00007f3b7cebf86c <unavailable> + 2091116
5 libswiftCore.so 0x00007f3b7cebd790 _NativeDictionary.resize(capacity:) + 1307
6 libswiftCore.so 0x00007f3b7ce54f10 _NativeDictionary.mutatingFind(_:isUnique:) + 453
7 libswiftCore.so 0x00007f3b7ce41760 Dictionary._Variant.setValue(_:forKey:) + 152
8 libswiftCore.so 0x00007f3b7d03e5ff <unavailable> + 3659263
9 libswiftCore.so 0x00007f3b7ce414c0 Dictionary.subscript.setter + 18
10 lint 0x000055bf470f28b3 <unavailable> + 108723
11 lint 0x000055bf470f3fa4 <unavailable> + 114596
12 lint 0x000055bf4711fa03 <unavailable> + 293379
13 lint 0x000055bf470f7c09 <unavailable> + 130057
14 lint 0x000055bf4711bf84 <unavailable> + 278404
15 libc.so.6 0x00007f3b7afa5740 __libc_start_main + 240
16 lint 0x000055bf470e3d89 <unavailable> + 48521
14:52:43.221: ℹ️ Start linting using config file at /bitrise/src/lint.swift ...
14:52:59.821: ❌ Linting failed using config file at /bitrise/src/lint.swift.
The documentation is all in the README and lacks structure for different use cases.
DocC provides a good "GettingStarted.md" file and the option to provide multiple tutorials for different use cases. It should be used, hosted and linked to in the README.
Linux support might theoretically work, but isn't tested. Also, there is no instructions on how to install it on Linux.
Could be based on swift-sh edit, which didn't work for me on first try though. More investigation needed.
At the moment, the file search is done for each check separately. Also, there's no shortcuts, it's always traversing all files. We could improve performance in large projects by doing the following:
The latter might be an alternative for the include
and exclude
options where instead of Regexes a new type could be provided, which already includes all the file paths and has made a search before. Then this can be reused for multiple checks. Basically, this would move the search out of the checks and make it an own API.
Currently, when auto-correction is provided for a rule, it's always running it, even when run via build-script in Xcode.
Provide an option to lint only, without auto-correcting, e.g. --no-autocorrect
/ -n
.
As of now, when a regex like #"(^|\n)#[^#](.*\n)*\n#[^#]"#
is specified which matches whitespaces or some kind of prefix first, where the actual problematic part lies somewhere in the middle of the matched string, the violation is still reported at the beginning of the matched string. This should be configurable, for example by providing a specific capture group name like pointer
reserved for the violation location.
Also, we could consider to support multiple violating locations that are related, this way lint checks could for example point out duplicate code showing both (or all) locations of the duplicated code.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.