Added vscode settings

This commit is contained in:
Kristofers Solo
2022-04-28 20:54:44 +03:00
parent 245c3ca779
commit 837a479d82
25004 changed files with 2499800 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
name: Bug report
description: Report a bug in Godot Tools VS Code add-on
labels:
- bug
body:
- type: markdown
attributes:
value: |
- Write a descriptive issue title above.
- Search [open](https://github.com/godotengine/godot-vscode-plugin/issues) and [closed](https://github.com/godotengine/godot-vscode-plugin/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported.
- Verify that you are using a [supported Godot version](https://docs.godotengine.org/en/stable/about/release_policy.html).
- This repository only contains the LSP *client*. Issues pertaining to the Godot editor itself or the LSP's *server* side should be reported on the [main Godot repository](https://github.com/godotengine/godot/issues).
- type: input
attributes:
label: Godot version
description: >
Specify the Git commit hash if using a development or non-official build.
If you use a custom build, please test if your issue is reproducible in official builds too.
placeholder: 3.3.stable, 4.0.dev (3041becc6)
validations:
required: true
- type: input
attributes:
label: VS Code version
description: >
Use the **Help > About** menu to see your current version.
Specify the Git commit hash if using a development or non-official build.
If you use a custom build, please test if your issue is reproducible in official builds too.
placeholder: "1.64.2"
validations:
required: true
- type: input
attributes:
label: Godot Tools VS Code extension version
description: >
Open the **Extensions** side panel and click on the **godot-tools** extension to see your current version.
Specify the Git commit hash if using a development or non-official build.
If you use a custom build, please test if your issue is reproducible in official builds too.
placeholder: "1.2.0"
validations:
required: true
- type: input
attributes:
label: System information
description: |
Specify the OS version, and when relevant hardware information.
placeholder: Windows 10
validations:
required: true
- type: textarea
attributes:
label: Issue description
description: |
Describe your issue briefly. What doesn't work, and how do you expect it to work instead?
You can include images or videos with drag and drop, and format code blocks or logs with <code>```</code> tags.
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: |
List of steps or sample code that reproduces the issue. Having reproducible issues is a prerequisite for contributors to be able to solve them.
validations:
required: true

View File

@@ -0,0 +1,6 @@
blank_issues_enabled: false
contact_links:
- name: Godot community channels
url: https://godotengine.org/community
about: Please ask for technical support on one of the other community channels, not here.

View File

@@ -0,0 +1,71 @@
name: Feature request
description: Request a new feature to be added or improved in Godot Tools VS Code add-on
labels:
- enhancement
body:
- type: markdown
attributes:
value: |
- Write a descriptive issue title above.
- Search [open](https://github.com/godotengine/godot-vscode-plugin/issues) and [closed](https://github.com/godotengine/godot-vscode-plugin/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported.
- Verify that you are using a [supported Godot version](https://docs.godotengine.org/en/stable/about/release_policy.html).
- This repository only contains the LSP *client*. Issues pertaining to the Godot editor itself or the LSP's *server* side should be reported on the [main Godot repository](https://github.com/godotengine/godot/issues).
- type: input
attributes:
label: Godot version
description: >
Specify the Git commit hash if using a development or non-official build.
If you use a custom build, please test if your issue is reproducible in official builds too.
placeholder: 3.3.stable, 4.0.dev (3041becc6)
validations:
required: true
- type: input
attributes:
label: VS Code version
description: >
Use the **Help > About** menu to see your current version.
Specify the Git commit hash if using a development or non-official build.
If you use a custom build, please test if your issue is reproducible in official builds too.
placeholder: "1.64.2"
validations:
required: true
- type: input
attributes:
label: Godot Tools VS Code extension version
description: >
Open the **Extensions** side panel and click on the **godot-tools** extension to see your current version.
Specify the Git commit hash if using a development or non-official build.
If you use a custom build, please test if your issue is reproducible in official builds too.
placeholder: "1.2.0"
validations:
required: true
- type: input
attributes:
label: System information
description: |
Specify the OS version, and when relevant hardware information.
placeholder: Windows 10
validations:
required: true
- type: textarea
attributes:
label: Problem statement
description: |
Describe the problem or limitation you're currently facing with the Godot Tools extension.
validations:
required: true
- type: textarea
attributes:
label: Proposed solution
description: |
Describe your proposed solution and how it resolves the problem or limitation mentioned above.
You can include images or videos with drag and drop, and format code blocks or logs with <code>```</code> tags.
validations:
required: true

View File

@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

View File

@@ -0,0 +1,27 @@
name: Continuous integration
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Node.js
uses: actions/setup-node@v3.1.0
with:
node-version: 16.x
- name: Lint and build extension
run: |
npm install
npm run lint
npm run package -- --out godot-tools.vsix
ls -l godot-tools.vsix
- name: Upload extension VSIX
uses: actions/upload-artifact@v2
with:
name: godot-tools
path: godot-tools.vsix

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Language="en-US" Id="godot-tools" Version="1.2.0" Publisher="geequlim" />
<DisplayName>godot-tools</DisplayName>
<Description xml:space="preserve">Tools for game development with Godot Engine and GDScript</Description>
<Tags>snippet,debuggers,gdscript,GDScript,__ext_gd,properties,__ext_cfg,__ext_tres,__ext_tscn,__ext_godot,__ext_gdns,__ext_gdnlib,__ext_import</Tags>
<Categories>Programming Languages,Linters,Snippets,Debuggers,Other</Categories>
<GalleryFlags>Public</GalleryFlags>
<Properties>
<Property Id="Microsoft.VisualStudio.Code.Engine" Value="^1.33.0" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionDependencies" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionPack" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionKind" Value="workspace" />
<Property Id="Microsoft.VisualStudio.Code.LocalizedLanguages" Value="" />
<Property Id="Microsoft.VisualStudio.Services.Links.Source" Value="https://github.com/godotengine/godot-vscode-plugin.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Getstarted" Value="https://github.com/godotengine/godot-vscode-plugin.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.GitHub" Value="https://github.com/godotengine/godot-vscode-plugin.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Support" Value="https://github.com/godotengine/godot-vscode-plugin/issues" />
<Property Id="Microsoft.VisualStudio.Services.Links.Learn" Value="https://github.com/godotengine/godot-vscode-plugin#readme" />
<Property Id="Microsoft.VisualStudio.Services.GitHubFlavoredMarkdown" Value="true" />
</Properties>
<License>extension/LICENSE.txt</License>
<Icon>extension/icon.png</Icon>
</Metadata>
<Installation>
<InstallationTarget Id="Microsoft.VisualStudio.Code"/>
</Installation>
<Dependencies/>
<Assets>
<Asset Type="Microsoft.VisualStudio.Code.Manifest" Path="extension/package.json" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Details" Path="extension/README.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Changelog" Path="extension/CHANGELOG.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.License" Path="extension/LICENSE.txt" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Icons.Default" Path="extension/icon.png" Addressable="true" />
</Assets>
</PackageManifest>

View File

@@ -0,0 +1,222 @@
# Change Log
### 1.2.0
* [Add support for setting language-server-host](https://github.com/godotengine/godot-vscode-plugin/pull/297)
* [Improve syntax highlighting](https://github.com/godotengine/godot-vscode-plugin/pull/330)
* [Update LSP client to 7.0.0 to use the 3.16.0 specification](https://github.com/godotengine/godot-vscode-plugin/pull/264)
* [Fix some `$` node path shorthand regex bugs in syntax highlighting](https://github.com/godotengine/godot-vscode-plugin/pull/340)
* [Fix handling of Windows terminals determined by profiles](https://github.com/godotengine/godot-vscode-plugin/pull/303)
* [Fix "static func" indent error](https://github.com/godotengine/godot-vscode-plugin/pull/279)
* [Fix restart of debugging sessions](https://github.com/godotengine/godot-vscode-plugin/pull/327)
* [Use the LSP defined SymbolKind enum and fix marked](https://github.com/godotengine/godot-vscode-plugin/pull/325)
* [Fix "Continue" for multiple breakpoints in the same script](https://github.com/godotengine/godot-vscode-plugin/pull/324)
### 1.1.3
* [Fix conditional breakpoints being parsed as regular breakpoints](https://github.com/godotengine/godot-vscode-plugin/pull/278)
- [Add `in` to the list of keywords and add rule for `$` shorthand](https://github.com/godotengine/godot-vscode-plugin/pull/274)
- [Fix typo in snippets: "decleration" -> "declaration"](https://github.com/godotengine/godot-vscode-plugin/pull/262)
- [Add `remote` keyword to syntax highlighting](https://github.com/godotengine/godot-vscode-plugin/pull/257)
- [Remove the configuration item `godot-tools.check_config` as it has no effect](https://github.com/godotengine/godot-vscode-plugin/pull/246)
- [Fix the syntax of escaped characters in strings](https://github.com/godotengine/godot-vscode-plugin/pull/247)
### 1.1.1
* Fix bug for GDScript debugger
* Add TCP protocol support for GDScript language server Godot 3.2.2
### 1.1
* Add the debugger to the extension
### 1.0.3
* Fix hover popup position for VSCode 1.42+
### 1.0.1
* Fix run editor error on windows with default terminal configurations
### 1.0.0
* Refactor the whole plugin with gdscript language server support
* Add webview renderer to show documentations of native symbols.
* Only support godot 3.2 and above
### 0.3.7
* Add `lint` configuration to control the behaviors of syntax checking
* Fix error with run godot editor when the editor contains spaces
* Disable semicolons and brackets checks as default can be enabled with project settings
* Fix bugs in syntax valiadating
* Sync documentations with godot 3.0.4
```json
{
"lint": {
"semicolon": true,
"conditionBrackets": true
}
}
```
### 0.3.6
* Fix project configuration file path
### 0.3.5
* Add option to disable syntax checking for GDScript
* Improved inline if else statement syntax checking
* More resource type supported for syntax highglight
* Bump default godot version to 3.0
* Sync the documentations from godot 3.0
### 0.3.4
* Fix bug with builtin symbols parsing for godot 2.1
* Improved hover documentation
* Show window progress when parsing workspace symbols
### 0.3.3
* Fix some syntax checking errors.
* Fix problems with hover documentation with latest VSCode.
* Improved builtin class documentation page.
* Update the documentation data with latest godot version.
### 0.3.2
* Fix syntax checking error with match statement.
* Improved documentation for builtin code blocks.
* Start using MarkdonwString to keep links valid.
### 0.3.1
* Update documentations with latest godot.
* Fix errors with run script and run project.
* Improve code completion with opening script file and constants.
* Some improvements for documentations.
### 0.3.0
* Add project root configuration settings as `GodotTools.godotProjectRoot` thanks Konstantin Zaitcev
* Add auto indent support for gdscript language
* More friendly with godot 3.0 alpha
* Updated script snippets
* Fix highglight error with gdscript language
* Limited code completions
### 0.2.9
* Add configuration `GodotTools.completeNodePath` to switch is complete node paths
* Enhanced syntax highlight with GDScript
* Enhanced code completion with GDScript
### 0.2.8
* Add godot 3.0 project support with configuration `GodotTools.parseTextScene` >= 3
* Add configuration `GodotTools.parseTextScene` to allow disable node path parsing
* Remove `GodotTools.editorServerPort` configuration
### 0.2.7
* Fix some error with syntax checking
* Add symbol support for enumerations
* Remove key bindings for `F5`~`F8` as it might be conflict with other functionalities of VSCode
* You can bind the key bindings back by add following configurations
```json
{
"command": "godot.runWorkspace",
"key": "F5"
},
{
"command": "godot.runCurrentScene",
"key": "F6"
},
{
"command": "godot.openWithEditor",
"key": "F7"
},
{
"command": "godot.updateWorkspaceSymbols",
"key": "F8"
}
```
For more references please ready [keybindings](https://code.visualstudio.com/docs/getstarted/keybindings)
### 0.2.6
* Add shorthand if else expression support
* Add `enum` and `match` expression support
* Fix bugs with syntax checking
* Updated documentation data with godot 2.1.3
* Add syntax checking for end of expression
* The pulugin is compiled with latest VSCode thanks @arrkiin
* Add key bindings for open workspace with godot editor with `F7` and update workspace symbols with `F8`
### 0.2.5
* Run games within VSCode terminals
* Add key bindings for `F5 to run the workspace` and `F6 to run the edting scene`
* Fix a lot of bugs with unused variable checking
* Move workspace symbols state notice to status bar
### 0.2.4
* Add code checking for asignments and comparisons
* Improved builtin documentation preview page
* Fix bugs with unused variable checking
### 0.2.3
* Fix known errors with code syntax checking
* Add configuration `ignoreIndentedVars` to allow ignore indented variables in scripts
* Enhanced hover tip documentation rendering with code examples
* Add launch configurations to launch game with F5(expiremental)
### 0.2.2
* Better Syntax validating for code blocks
* More warning for non-python liked expression
### 0.2.1
* Support markdown render in hover tips for documentations in workspace symbols
* Add configuration `GodotTools.workspaceDocumentWithMarkdown` to control workspace documentation rendering
### 0.2.0
* Show autoloads information in hover tips and go to autoloads' definitions are supported now
* Fix the bug that workspace symbols resoved twice on Windows
### 0.1.9
* Show workspace constant value in hover tips and completion items
* More readable style for links in documentation preview page
* Improve code completion sort order and auto insert `()` for functions without parameters
* Fix bugs with workspace documentation parsing
### 0.1.8
* Show signatures on completion label
* More reliable unused variable and constant checking in documente
* Show workspace documentations and function signatures in completions
### 0.1.7
* Show documentations parsed from GDScripts in hover tips
### 0.1.6
* Reorder mouse hover tips, builtin methods are at top of workspace methods
* Show callabel signatures with documente symbols and workspace symbols
* Syntax highlight support for signal parameters
### 0.1.5
* Add function signature hint support
* Better syntax grammar checking
* Better hover hint message for workspace methods and signals
### 0.1.4
* Add documentation support for builtin Symbols.
* Improve speed of syntax parsing and other actions
### 0.1.3
* Better syntax highlight for GDScript
* Add mouse hover information support
* Add definition provider for GDScript
### 0.1.2
* Multiline string and `StringName` highlight support
* Builtin classes, properties, functions and constants highlight support
* Fix errors in code snipt
### 0.1.1
* Better syntax highlight with GDScript
### 0.1.0
* Initial release

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016-2022 The Godot Engine community
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,131 @@
# Godot Tools
A complete set of tools to code games with
[Godot Engine](http://www.godotengine.org/) in Visual Studio Code.
**IMPORTANT NOTE:** Versions 1.0.0 and later of this plugin only support
Godot 3.2 or later.
## Features
The extension comes with a wealth of features to make your Godot programming
experience as comfortable as possible:
- Syntax highlighting for the GDScript (`.gd`) language
- Syntax highlighting for the `.tscn` and `.tres` scene formats
- Full typed GDScript support
- Optional "Smart Mode" to improve productivity with dynamically typed scripts
- Function definitions and documentation display on hover (see image below)
- Rich autocompletion
- Display script warnings and errors
- Ctrl + click on a variable or method call to jump to its definition
- Full documentation of the Godot Engine's API supported (select *Godot Tools: List native classes of Godot* in the Command Palette)
- Run a Godot project from VS Code
- Debug your GDScript-based Godot project from VS Code with breakpoints, step-in/out/over, variable watch, call stack, and active scene tree
![Showing the documentation on hover feature](https://github.com/godotengine/godot-vscode-plugin/raw/HEAD/img/godot-tools.png)
## Available commands
The extension adds a few entries to the VS Code Command Palette under "Godot Tools":
- Open workspace with Godot editor
- Run the workspace as a Godot project
- List Godot's native classes
## Settings
### Godot
If you like this extension, you can set VS Code as your default script editor
for Godot by following these steps:
1. Open the **Editor Settings**
2. Select **Text Editor > External**
3. Make sure the **Use External Editor** box is checked
4. Fill **Exec Path** with the path to your VS Code executable
* On macOS, this executable is typically located at: `/Applications/Visual Studio Code.app/Contents/MacOS/Electron`
5. Fill **Exec Flags** with `{project} --goto {file}:{line}:{col}`
### VS Code
You can use the following settings to configure Godot Tools:
- `editor_path` - The absolute path to the Godot editor executable. _Under Mac OS, this is the executable inside of Godot.app._
- `gdscript_lsp_server_port` - The WebSocket server port of the GDScript language server.
- `check_status` - Check the GDScript language server connection status.
#### GDScript Debugger
The debugger is for GDScript projects. To debug C# projects, use [C# Tools for Godot](https://github.com/godotengine/godot-csharp-vscode).
To configure the GDScript debugger:
1. Open the command palette:
2. `>Debug: Open launch.json`
3. Select the Debug Godot configuration.
4. Change any relevant settings.
5. Press F5 to launch.
*Configurations*
_Required_
- "project": Absolute path to a directory with a project.godot file. Defaults to the currently open VSCode workspace with `${workspaceFolder}`.
- "port": Number that represents the port the Godot remote debugger will connect with. Defaults to `6007`.
- "address": String that represents the IP address that the Godot remote debugger will connect to. Defaults to `127.0.0.1`.
_Optional_
- "launch_game_instance": true/false. If true, an instance of Godot will be launched. Will use the path provided in `editor_path`. Defaults to `true`.
- "launch_scene": true/false. If true, and launch_game_instance is true, will launch an instance of Godot to a currently active opened TSCN file. Defaults to `false`.
- "scene_file": Path _relative to the project.godot file_ to a TSCN file. If launch_game_instance and launch_scene are both true, will use this file instead of looking for the currently active opened TSCN file.
*Usage*
- Stacktrace and variable dumps are the same as any regular debugger
- The active scene tree can be refreshed with the Refresh icon in the top right.
- Nodes can be brought to the fore in the Inspector by clicking the Eye icon next to nodes in the active scene tree, or Objects in the inspector.
- You can edit integers, floats, strings, and booleans within the inspector by clicking the pencil icon next to each.
![Showing the debugger in action](https://github.com/godotengine/godot-vscode-plugin/raw/HEAD/img/godot-debug.png)
## Issues and contributions
The [Godot Tools](https://github.com/godotengine/godot-vscode-plugin) extension
is an open source project from the Godot organization. Feel free to open issues
and create pull requests anytime.
See the [full changelog](https://github.com/GodotExplorer/godot-tools/blob/master/CHANGELOG.md)
for the latest changes.
### Building from source
#### Requirements
- [npm](https://www.npmjs.com/get-npm)
#### Process
1. Open a command prompt/terminal and browse to the location of this repository on your local filesystem.
2. Download dependencies by using the command `npm install`
3. When done, package a VSIX file by using the command `npm run package`.
4. Install it by opening Visual Studio Code, opening the Extensions tab, clicking on the More actions (**...**) button in the top right, and choose **Install from VSIX...** and find the compiled VSIX file.
When developing for the extension, you can open this project in Visual Studio Code and debug the extension by using the **Run Extension** launch configuration instead of going through steps 3 and 4. It will launch a new instance of Visual Studio Code that has the extension running. You can then open a Godot project folder and debug the extension or GDScript debugger.
## FAQ
### Why does it fail to connect to the language server?
- Godot 3.2 or later is required.
- Make sure to open the project in the Godot editor first. If you opened
the editor after opening VS Code, you can click the **Retry** button
in the bottom-right corner in VS Code.
### Why isn't IntelliSense displaying script members?
- GDScript is a dynamically typed script language. The language server can't
infer all variable types.
- To increase the number of results displayed, open the **Editor Settings**,
go to the **Language Server** section then check **Enable Smart Resolve**.

View File

@@ -0,0 +1,36 @@
{
"comments": {
"lineComment": "#",
"blockComment": ["\"\"\"", "\"\"\""]
},
"brackets": [
["(", ")"],
["[", "]"],
["{", "}"]
],
"autoClosingPairs": [
["'", "'"],
["\"", "\""],
["(", ")"],
["[", "]"],
["{", "}"]
],
"surroundingPairs": [
["'", "'"],
["\"", "\""],
["(", ")"],
["[", "]"],
["{", "}"]
],
"indentationRules": {
"increaseIndentPattern": "^\\s*((class|static func|func|else|elif|for|if|match|while|enum)|(.*\\sdo\\b))\\b[^\\{;]*$",
"decreaseIndentPattern": "^\\s*([}\\]]([,)]?\\s*(#|$)|\\.[a-zA-Z_]\\w*\\b)|(else|elif)\\b)"
},
"folding": {
"offSide": true,
"markers": {
"start": "^\\s*#\\s*region\\b",
"end": "^\\s*#\\s*endregion\\b"
}
}
}

View File

@@ -0,0 +1,200 @@
{
"Inner class": {
"prefix": "class",
"body": [
"class $1 extends ${2:Reference}",
"\t$3"
]
},
"Print messages to console": {
"prefix": "pr",
"body": [
"print($1)"
]
},
"_ready method of Node": {
"prefix": "ready",
"body": [
"func _ready():",
"\t${1:pass}"
]
},
"_init method of Object": {
"prefix": "init",
"body": [
"func _init():",
"\t${1:pass}"
]
},
"_process method of Node": {
"prefix": "process",
"body": [
"func _process(delta):",
"\t${1:pass}"
]
},
"_input method of Node": {
"prefix": "input",
"body": [
"func _input(event):",
"\t${1:pass}"
]
},
"_input_event method of Node": {
"prefix": "inpute",
"body": [
"func _input_event(event):",
"\t${1:pass}"
]
},
"_draw method of Node": {
"prefix": "draw",
"body": [
"func _draw():",
"\t${1:pass}"
]
},
"_gui_input method of Node": {
"prefix": "guii",
"body": [
"func _gui_input(event):",
"\t${1:pass}"
]
},
"for loop": {
"prefix": "for",
"body": [
"for $1 in $2:",
"\t${3:pass}"
]
},
"for range loop": {
"prefix": "for",
"body": [
"for $1 in range(${2:start}{$3:,end}):",
"\t${4:pass}"
]
},
"if elif else": {
"prefix": "if",
"body": [
"if ${1:condition}:",
"\t${3:pass}",
"elif ${2:condition}:",
"\t${4:pass}",
"else:",
"\t${5:pass}"
]
},
"if else": {
"prefix": "if",
"body": [
"if ${1:condition}:",
"\t${2:pass}",
"else:",
"\t${3:pass}"
]
},
"if": {
"prefix": "if",
"body": [
"if ${1:condition}:",
"\t${2:pass}"
]
},
"while": {
"prefix": "while",
"body": [
"while ${1:condition}:",
"\t${2:pass}"
]
},
"function define": {
"prefix": "func",
"body": [
"func ${1:method}(${2:args}):",
"\t${3:pass}"
]
},
"signal declaration": {
"prefix": "signal",
"body": [
"signal ${1:signalname}(${2:args})"
]
},
"export variables": {
"prefix": "export",
"body": [
"export(${1:type}${2:,other_configs}) var ${3:name}${4: = default}${5: setget }"
]
},
"define variables": {
"prefix": "var",
"body": [
"var ${1:name}${2: = default}${3: setget }"
]
},
"define onready variables": {
"prefix": "onready",
"body": [
"onready var ${1:name} = get_node($2)"
]
},
"Is instance of a class or script": {
"prefix": "is",
"body": [
"${1:instance} is ${2:class}"
]
},
"element in array": {
"prefix": "in",
"body": [
"${1:element} in ${$2:array}"
]
},
"GDScript template": {
"prefix": "gdscript",
"body": [
"extends ${1:BaseClass}",
"",
"# class member variables go here, for example:",
"# var a = 2",
"# var b = \"textvar\"",
"",
"func _ready():",
"\t# Called every time the node is added to the scene.",
"\t# Initialization here",
"\tpass",
""
]
},
"pass statement": {
"prefix": "pass",
"body": [
"pass"
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

View File

@@ -0,0 +1,106 @@
Simple library providing Java like wait and notify functionality for async/await syntax.
Works in Node (or browser via Browserify or Webpack) when Promise and async/await syntax are enabled.
Install
=======
npm install --save await-notify
Usage
=====
const { Subject } = require('await-notify');
const event = new Subject();
(async () => {
while (true) {
await event.wait();
console.log('Event occured');
}
})();
setTimeout(() => {
event.notify();
}, 1000);
API reference
=============
Subject
-------
A class representing subjects that could be waited for and notfied on.
#### Subject.prototype.wait([timeout = Infinity])
Returns a promise that will be resolved either when the subject is been notfied or when timeout occurs.
`timeout` parameter is in milliseconds, and no timeout will occur if not set to an finite number greater than 0.
The promise resolves `false` if timeout occurs, resolves `true` otherwise.
const subject = new Subject();
(async () => {
const notTimeout = await subject.wait();
...
})();
#### Subject.prototype.notify()
Resolve ***one*** waiting session. Does nothing if no one is waiting on this subject.
Return nothing and thus should not be used with `await`.
subject.notify();
#### Subject.prototype.notifyAll()
Similar to `notify` but notifies ***all*** waiting session.
Tips
====
Pass data to the waiter when calling notify
-------------------------------------------
There's no fancy way to do this, but a Subject instance is just another plain JavaScript object which you
can freely assign parameters to. So you may just do this:
const { Subject } = require('await-notify');
const event = new Subject();
(async () => {
while (true) {
await event.wait();
console.log('Event occured with message:'. event.message);
}
})();
setTimeout(() => {
event.message = 'Hello there';
event.notify();
}, 1000);
Use without async/await
-----------------------
Under the hood, async/await are just sugar syntax for using Promise. So using promise directly is fine.
const { Subject } = require('await-notify');
const event = new Subject();
function listen() {
event.wait().then(() => {
console.log('Event occured');
listen();
});
}
listen();
setTimeout(() => {
event.message = 'Hello there';
event.notify();
}, 1000);

View File

@@ -0,0 +1,51 @@
function Subject() {
this.waiters = [];
}
Subject.prototype.wait = function (timeout) {
var self = this;
var waiter = {};
this.waiters.push(waiter);
var promise = new Promise(function (resolve) {
var resolved = false;
waiter.resolve = function (noRemove) {
if (resolved) {
return;
}
resolved = true;
if (waiter.timeout) {
clearTimeout(waiter.timeout);
waiter.timeout = null;
}
if (!noRemove) {
var pos = self.waiters.indexOf(waiter);
if (pos > -1) {
self.waiters.splice(pos, 1);
}
}
resolve();
};
});
if (timeout > 0 && isFinite(timeout)) {
waiter.timeout = setTimeout(function () {
waiter.timeout = null;
waiter.resolve();
}, timeout);
}
return promise;
};
Subject.prototype.notify = function () {
if (this.waiters.length > 0) {
this.waiters.pop().resolve(true);
}
};
Subject.prototype.notifyAll = function () {
for (var i = this.waiters.length - 1; i >= 0; i--) {
this.waiters[i].resolve(true);
}
this.waiters = [];
}
exports.Subject = Subject;

View File

@@ -0,0 +1,19 @@
{
"name": "await-notify",
"version": "1.0.1",
"description": "Java like wait and notify for JavaScript ES7 async/await",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/davidaq/node-await-notify.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/davidaq/node-await-notify/issues"
},
"homepage": "https://github.com/davidaq/node-await-notify#readme"
}

View File

@@ -0,0 +1,5 @@
test
.gitignore
.travis.yml
Makefile
example.js

View File

@@ -0,0 +1,21 @@
(MIT)
Copyright (c) 2013 Julian Gruber &lt;julian@juliangruber.com&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,91 @@
# balanced-match
Match balanced string pairs, like `{` and `}` or `<b>` and `</b>`. Supports regular expressions as well!
[![build status](https://secure.travis-ci.org/juliangruber/balanced-match.svg)](http://travis-ci.org/juliangruber/balanced-match)
[![downloads](https://img.shields.io/npm/dm/balanced-match.svg)](https://www.npmjs.org/package/balanced-match)
[![testling badge](https://ci.testling.com/juliangruber/balanced-match.png)](https://ci.testling.com/juliangruber/balanced-match)
## Example
Get the first matching pair of braces:
```js
var balanced = require('balanced-match');
console.log(balanced('{', '}', 'pre{in{nested}}post'));
console.log(balanced('{', '}', 'pre{first}between{second}post'));
console.log(balanced(/\s+\{\s+/, /\s+\}\s+/, 'pre { in{nest} } post'));
```
The matches are:
```bash
$ node example.js
{ start: 3, end: 14, pre: 'pre', body: 'in{nested}', post: 'post' }
{ start: 3,
end: 9,
pre: 'pre',
body: 'first',
post: 'between{second}post' }
{ start: 3, end: 17, pre: 'pre', body: 'in{nest}', post: 'post' }
```
## API
### var m = balanced(a, b, str)
For the first non-nested matching pair of `a` and `b` in `str`, return an
object with those keys:
* **start** the index of the first match of `a`
* **end** the index of the matching `b`
* **pre** the preamble, `a` and `b` not included
* **body** the match, `a` and `b` not included
* **post** the postscript, `a` and `b` not included
If there's no match, `undefined` will be returned.
If the `str` contains more `a` than `b` / there are unmatched pairs, the first match that was closed will be used. For example, `{{a}` will match `['{', 'a', '']` and `{a}}` will match `['', 'a', '}']`.
### var r = balanced.range(a, b, str)
For the first non-nested matching pair of `a` and `b` in `str`, return an
array with indexes: `[ <a index>, <b index> ]`.
If there's no match, `undefined` will be returned.
If the `str` contains more `a` than `b` / there are unmatched pairs, the first match that was closed will be used. For example, `{{a}` will match `[ 1, 3 ]` and `{a}}` will match `[0, 2]`.
## Installation
With [npm](https://npmjs.org) do:
```bash
npm install balanced-match
```
## License
(MIT)
Copyright (c) 2013 Julian Gruber &lt;julian@juliangruber.com&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,59 @@
'use strict';
module.exports = balanced;
function balanced(a, b, str) {
if (a instanceof RegExp) a = maybeMatch(a, str);
if (b instanceof RegExp) b = maybeMatch(b, str);
var r = range(a, b, str);
return r && {
start: r[0],
end: r[1],
pre: str.slice(0, r[0]),
body: str.slice(r[0] + a.length, r[1]),
post: str.slice(r[1] + b.length)
};
}
function maybeMatch(reg, str) {
var m = str.match(reg);
return m ? m[0] : null;
}
balanced.range = range;
function range(a, b, str) {
var begs, beg, left, right, result;
var ai = str.indexOf(a);
var bi = str.indexOf(b, ai + 1);
var i = ai;
if (ai >= 0 && bi > 0) {
begs = [];
left = str.length;
while (i >= 0 && !result) {
if (i == ai) {
begs.push(i);
ai = str.indexOf(a, i + 1);
} else if (begs.length == 1) {
result = [ begs.pop(), bi ];
} else {
beg = begs.pop();
if (beg < left) {
left = beg;
right = bi;
}
bi = str.indexOf(b, i + 1);
}
i = ai < bi && ai >= 0 ? ai : bi;
}
if (begs.length) {
result = [ left, right ];
}
}
return result;
}

View File

@@ -0,0 +1,49 @@
{
"name": "balanced-match",
"description": "Match balanced character pairs, like \"{\" and \"}\"",
"version": "1.0.0",
"repository": {
"type": "git",
"url": "git://github.com/juliangruber/balanced-match.git"
},
"homepage": "https://github.com/juliangruber/balanced-match",
"main": "index.js",
"scripts": {
"test": "make test",
"bench": "make bench"
},
"dependencies": {},
"devDependencies": {
"matcha": "^0.7.0",
"tape": "^4.6.0"
},
"keywords": [
"match",
"regexp",
"test",
"balanced",
"parse"
],
"author": {
"name": "Julian Gruber",
"email": "mail@juliangruber.com",
"url": "http://juliangruber.com"
},
"license": "MIT",
"testling": {
"files": "test/*.js",
"browsers": [
"ie/8..latest",
"firefox/20..latest",
"firefox/nightly",
"chrome/25..latest",
"chrome/canary",
"opera/12..latest",
"opera/next",
"safari/5.1..latest",
"ipad/6.0..latest",
"iphone/6.0..latest",
"android-browser/4.2..latest"
]
}
}

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,129 @@
# brace-expansion
[Brace expansion](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html),
as known from sh/bash, in JavaScript.
[![build status](https://secure.travis-ci.org/juliangruber/brace-expansion.svg)](http://travis-ci.org/juliangruber/brace-expansion)
[![downloads](https://img.shields.io/npm/dm/brace-expansion.svg)](https://www.npmjs.org/package/brace-expansion)
[![Greenkeeper badge](https://badges.greenkeeper.io/juliangruber/brace-expansion.svg)](https://greenkeeper.io/)
[![testling badge](https://ci.testling.com/juliangruber/brace-expansion.png)](https://ci.testling.com/juliangruber/brace-expansion)
## Example
```js
var expand = require('brace-expansion');
expand('file-{a,b,c}.jpg')
// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg']
expand('-v{,,}')
// => ['-v', '-v', '-v']
expand('file{0..2}.jpg')
// => ['file0.jpg', 'file1.jpg', 'file2.jpg']
expand('file-{a..c}.jpg')
// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg']
expand('file{2..0}.jpg')
// => ['file2.jpg', 'file1.jpg', 'file0.jpg']
expand('file{0..4..2}.jpg')
// => ['file0.jpg', 'file2.jpg', 'file4.jpg']
expand('file-{a..e..2}.jpg')
// => ['file-a.jpg', 'file-c.jpg', 'file-e.jpg']
expand('file{00..10..5}.jpg')
// => ['file00.jpg', 'file05.jpg', 'file10.jpg']
expand('{{A..C},{a..c}}')
// => ['A', 'B', 'C', 'a', 'b', 'c']
expand('ppp{,config,oe{,conf}}')
// => ['ppp', 'pppconfig', 'pppoe', 'pppoeconf']
```
## API
```js
var expand = require('brace-expansion');
```
### var expanded = expand(str)
Return an array of all possible and valid expansions of `str`. If none are
found, `[str]` is returned.
Valid expansions are:
```js
/^(.*,)+(.+)?$/
// {a,b,...}
```
A comma separated list of options, like `{a,b}` or `{a,{b,c}}` or `{,a,}`.
```js
/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/
// {x..y[..incr]}
```
A numeric sequence from `x` to `y` inclusive, with optional increment.
If `x` or `y` start with a leading `0`, all the numbers will be padded
to have equal length. Negative numbers and backwards iteration work too.
```js
/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/
// {x..y[..incr]}
```
An alphabetic sequence from `x` to `y` inclusive, with optional increment.
`x` and `y` must be exactly one character, and if given, `incr` must be a
number.
For compatibility reasons, the string `${` is not eligible for brace expansion.
## Installation
With [npm](https://npmjs.org) do:
```bash
npm install brace-expansion
```
## Contributors
- [Julian Gruber](https://github.com/juliangruber)
- [Isaac Z. Schlueter](https://github.com/isaacs)
## Sponsors
This module is proudly supported by my [Sponsors](https://github.com/juliangruber/sponsors)!
Do you want to support modules like this to improve their quality, stability and weigh in on new features? Then please consider donating to my [Patreon](https://www.patreon.com/juliangruber). Not sure how much of my modules you're using? Try [feross/thanks](https://github.com/feross/thanks)!
## License
(MIT)
Copyright (c) 2013 Julian Gruber &lt;julian@juliangruber.com&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,201 @@
var concatMap = require('concat-map');
var balanced = require('balanced-match');
module.exports = expandTop;
var escSlash = '\0SLASH'+Math.random()+'\0';
var escOpen = '\0OPEN'+Math.random()+'\0';
var escClose = '\0CLOSE'+Math.random()+'\0';
var escComma = '\0COMMA'+Math.random()+'\0';
var escPeriod = '\0PERIOD'+Math.random()+'\0';
function numeric(str) {
return parseInt(str, 10) == str
? parseInt(str, 10)
: str.charCodeAt(0);
}
function escapeBraces(str) {
return str.split('\\\\').join(escSlash)
.split('\\{').join(escOpen)
.split('\\}').join(escClose)
.split('\\,').join(escComma)
.split('\\.').join(escPeriod);
}
function unescapeBraces(str) {
return str.split(escSlash).join('\\')
.split(escOpen).join('{')
.split(escClose).join('}')
.split(escComma).join(',')
.split(escPeriod).join('.');
}
// Basically just str.split(","), but handling cases
// where we have nested braced sections, which should be
// treated as individual members, like {a,{b,c},d}
function parseCommaParts(str) {
if (!str)
return [''];
var parts = [];
var m = balanced('{', '}', str);
if (!m)
return str.split(',');
var pre = m.pre;
var body = m.body;
var post = m.post;
var p = pre.split(',');
p[p.length-1] += '{' + body + '}';
var postParts = parseCommaParts(post);
if (post.length) {
p[p.length-1] += postParts.shift();
p.push.apply(p, postParts);
}
parts.push.apply(parts, p);
return parts;
}
function expandTop(str) {
if (!str)
return [];
// I don't know why Bash 4.3 does this, but it does.
// Anything starting with {} will have the first two bytes preserved
// but *only* at the top level, so {},a}b will not expand to anything,
// but a{},b}c will be expanded to [a}c,abc].
// One could argue that this is a bug in Bash, but since the goal of
// this module is to match Bash's rules, we escape a leading {}
if (str.substr(0, 2) === '{}') {
str = '\\{\\}' + str.substr(2);
}
return expand(escapeBraces(str), true).map(unescapeBraces);
}
function identity(e) {
return e;
}
function embrace(str) {
return '{' + str + '}';
}
function isPadded(el) {
return /^-?0\d/.test(el);
}
function lte(i, y) {
return i <= y;
}
function gte(i, y) {
return i >= y;
}
function expand(str, isTop) {
var expansions = [];
var m = balanced('{', '}', str);
if (!m || /\$$/.test(m.pre)) return [str];
var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
var isSequence = isNumericSequence || isAlphaSequence;
var isOptions = m.body.indexOf(',') >= 0;
if (!isSequence && !isOptions) {
// {a},b}
if (m.post.match(/,.*\}/)) {
str = m.pre + '{' + m.body + escClose + m.post;
return expand(str);
}
return [str];
}
var n;
if (isSequence) {
n = m.body.split(/\.\./);
} else {
n = parseCommaParts(m.body);
if (n.length === 1) {
// x{{a,b}}y ==> x{a}y x{b}y
n = expand(n[0], false).map(embrace);
if (n.length === 1) {
var post = m.post.length
? expand(m.post, false)
: [''];
return post.map(function(p) {
return m.pre + n[0] + p;
});
}
}
}
// at this point, n is the parts, and we know it's not a comma set
// with a single entry.
// no need to expand pre, since it is guaranteed to be free of brace-sets
var pre = m.pre;
var post = m.post.length
? expand(m.post, false)
: [''];
var N;
if (isSequence) {
var x = numeric(n[0]);
var y = numeric(n[1]);
var width = Math.max(n[0].length, n[1].length)
var incr = n.length == 3
? Math.abs(numeric(n[2]))
: 1;
var test = lte;
var reverse = y < x;
if (reverse) {
incr *= -1;
test = gte;
}
var pad = n.some(isPadded);
N = [];
for (var i = x; test(i, y); i += incr) {
var c;
if (isAlphaSequence) {
c = String.fromCharCode(i);
if (c === '\\')
c = '';
} else {
c = String(i);
if (pad) {
var need = width - c.length;
if (need > 0) {
var z = new Array(need + 1).join('0');
if (i < 0)
c = '-' + z + c.slice(1);
else
c = z + c;
}
}
}
N.push(c);
}
} else {
N = concatMap(n, function(el) { return expand(el, false) });
}
for (var j = 0; j < N.length; j++) {
for (var k = 0; k < post.length; k++) {
var expansion = pre + N[j] + post[k];
if (!isTop || isSequence || expansion)
expansions.push(expansion);
}
}
return expansions;
}

View File

@@ -0,0 +1,47 @@
{
"name": "brace-expansion",
"description": "Brace expansion as known from sh/bash",
"version": "1.1.11",
"repository": {
"type": "git",
"url": "git://github.com/juliangruber/brace-expansion.git"
},
"homepage": "https://github.com/juliangruber/brace-expansion",
"main": "index.js",
"scripts": {
"test": "tape test/*.js",
"gentest": "bash test/generate.sh",
"bench": "matcha test/perf/bench.js"
},
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
},
"devDependencies": {
"matcha": "^0.7.0",
"tape": "^4.6.0"
},
"keywords": [],
"author": {
"name": "Julian Gruber",
"email": "mail@juliangruber.com",
"url": "http://juliangruber.com"
},
"license": "MIT",
"testling": {
"files": "test/*.js",
"browsers": [
"ie/8..latest",
"firefox/20..latest",
"firefox/nightly",
"chrome/25..latest",
"chrome/canary",
"opera/12..latest",
"opera/next",
"safari/5.1..latest",
"ipad/6.0..latest",
"iphone/6.0..latest",
"android-browser/4.2..latest"
]
}
}

View File

@@ -0,0 +1,4 @@
language: node_js
node_js:
- 0.4
- 0.6

View File

@@ -0,0 +1,18 @@
This software is released under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,62 @@
concat-map
==========
Concatenative mapdashery.
[![browser support](http://ci.testling.com/substack/node-concat-map.png)](http://ci.testling.com/substack/node-concat-map)
[![build status](https://secure.travis-ci.org/substack/node-concat-map.png)](http://travis-ci.org/substack/node-concat-map)
example
=======
``` js
var concatMap = require('concat-map');
var xs = [ 1, 2, 3, 4, 5, 6 ];
var ys = concatMap(xs, function (x) {
return x % 2 ? [ x - 0.1, x, x + 0.1 ] : [];
});
console.dir(ys);
```
***
```
[ 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1 ]
```
methods
=======
``` js
var concatMap = require('concat-map')
```
concatMap(xs, fn)
-----------------
Return an array of concatenated elements by calling `fn(x, i)` for each element
`x` and each index `i` in the array `xs`.
When `fn(x, i)` returns an array, its result will be concatenated with the
result array. If `fn(x, i)` returns anything else, that value will be pushed
onto the end of the result array.
install
=======
With [npm](http://npmjs.org) do:
```
npm install concat-map
```
license
=======
MIT
notes
=====
This module was written while sitting high above the ground in a tree.

View File

@@ -0,0 +1,6 @@
var concatMap = require('../');
var xs = [ 1, 2, 3, 4, 5, 6 ];
var ys = concatMap(xs, function (x) {
return x % 2 ? [ x - 0.1, x, x + 0.1 ] : [];
});
console.dir(ys);

View File

@@ -0,0 +1,13 @@
module.exports = function (xs, fn) {
var res = [];
for (var i = 0; i < xs.length; i++) {
var x = fn(xs[i], i);
if (isArray(x)) res.push.apply(res, x);
else res.push(x);
}
return res;
};
var isArray = Array.isArray || function (xs) {
return Object.prototype.toString.call(xs) === '[object Array]';
};

View File

@@ -0,0 +1,43 @@
{
"name" : "concat-map",
"description" : "concatenative mapdashery",
"version" : "0.0.1",
"repository" : {
"type" : "git",
"url" : "git://github.com/substack/node-concat-map.git"
},
"main" : "index.js",
"keywords" : [
"concat",
"concatMap",
"map",
"functional",
"higher-order"
],
"directories" : {
"example" : "example",
"test" : "test"
},
"scripts" : {
"test" : "tape test/*.js"
},
"devDependencies" : {
"tape" : "~2.4.0"
},
"license" : "MIT",
"author" : {
"name" : "James Halliday",
"email" : "mail@substack.net",
"url" : "http://substack.net"
},
"testling" : {
"files" : "test/*.js",
"browsers" : {
"ie" : [ 6, 7, 8, 9 ],
"ff" : [ 3.5, 10, 15.0 ],
"chrome" : [ 10, 22 ],
"safari" : [ 5.1 ],
"opera" : [ 12 ]
}
}
}

View File

@@ -0,0 +1,39 @@
var concatMap = require('../');
var test = require('tape');
test('empty or not', function (t) {
var xs = [ 1, 2, 3, 4, 5, 6 ];
var ixes = [];
var ys = concatMap(xs, function (x, ix) {
ixes.push(ix);
return x % 2 ? [ x - 0.1, x, x + 0.1 ] : [];
});
t.same(ys, [ 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1 ]);
t.same(ixes, [ 0, 1, 2, 3, 4, 5 ]);
t.end();
});
test('always something', function (t) {
var xs = [ 'a', 'b', 'c', 'd' ];
var ys = concatMap(xs, function (x) {
return x === 'b' ? [ 'B', 'B', 'B' ] : [ x ];
});
t.same(ys, [ 'a', 'B', 'B', 'B', 'c', 'd' ]);
t.end();
});
test('scalars', function (t) {
var xs = [ 'a', 'b', 'c', 'd' ];
var ys = concatMap(xs, function (x) {
return x === 'b' ? [ 'B', 'B', 'B' ] : x;
});
t.same(ys, [ 'a', 'B', 'B', 'B', 'c', 'd' ]);
t.end();
});
test('undefs', function (t) {
var xs = [ 'a', 'b', 'c', 'd' ];
var ys = concatMap(xs, function () {});
t.same(ys, [ undefined, undefined, undefined, undefined ]);
t.end();
});

View File

@@ -0,0 +1,19 @@
Copyright (c) 2012 Raynos.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,2 @@
run:
./node_modules/.bin/browserify-server --cwd example

View File

@@ -0,0 +1,23 @@
# dom-walk
iteratively walk a DOM node
## Example
``` js
var walk = require("dom-walk")
walk(document.body.childNodes, function (node) {
console.log("node", node)
})
```
## Installation
`npm install dom-walk`
## Contributors
- Raynos
## MIT Licenced

View File

@@ -0,0 +1,5 @@
var walk = require("../index")
walk(document, function (node) {
console.log("node", node)
})

View File

@@ -0,0 +1,24 @@
var slice = Array.prototype.slice
module.exports = iterativelyWalk
function iterativelyWalk(nodes, cb) {
if (!('length' in nodes)) {
nodes = [nodes]
}
nodes = slice.call(nodes)
while(nodes.length) {
var node = nodes.shift(),
ret = cb(node)
if (ret) {
return ret
}
if (node.childNodes && node.childNodes.length) {
nodes = slice.call(node.childNodes).concat(nodes)
}
}
}

View File

@@ -0,0 +1,32 @@
{
"name": "dom-walk",
"version": "0.1.2",
"description": "iteratively walk a DOM node",
"keywords": [],
"author": "Raynos <raynos2@gmail.com>",
"repository": "git://github.com/Raynos/dom-walk.git",
"main": "index",
"homepage": "https://github.com/Raynos/dom-walk",
"contributors": [
{
"name": "Jake Verbaten"
}
],
"bugs": {
"url": "https://github.com/Raynos/dom-walk/issues",
"email": "raynos2@gmail.com"
},
"dependencies": {},
"devDependencies": {
"budo": "11.6.3"
},
"licenses": [
{
"type": "MIT",
"url": "http://github.com/Raynos/dom-walk/raw/master/LICENSE"
}
],
"scripts": {
"example": "budo example/index.js"
}
}

View File

@@ -0,0 +1,3 @@
node_modules
*.log
*.err

View File

@@ -0,0 +1,6 @@
language: node_js
node_js:
- "0.11"
- "0.10"
- "0.8"
- "0.6"

View File

@@ -0,0 +1,19 @@
Copyright (c) 2012 Raynos.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,47 @@
# duplexer
[![build status][1]][2] [![dependency status][3]][4]
[![browser support][5]][6]
Creates a duplex stream
Taken from [event-stream][7]
## duplex (writeStream, readStream)
Takes a writable stream and a readable stream and makes them appear as a readable writable stream.
It is assumed that the two streams are connected to each other in some way.
## Example
```js
var grep = cp.exec('grep Stream')
duplex(grep.stdin, grep.stdout)
```
## Installation
`npm install duplexer`
## Tests
`npm test`
## Contributors
- Dominictarr
- Raynos
- samccone
## MIT Licenced
[1]: https://secure.travis-ci.org/Raynos/duplexer.png
[2]: https://travis-ci.org/Raynos/duplexer
[3]: https://david-dm.org/Raynos/duplexer.png
[4]: https://david-dm.org/Raynos/duplexer
[5]: https://ci.testling.com/Raynos/duplexer.png
[6]: https://ci.testling.com/Raynos/duplexer
[7]: https://github.com/dominictarr/event-stream#duplex-writestream-readstream

View File

@@ -0,0 +1,87 @@
var Stream = require("stream")
var writeMethods = ["write", "end", "destroy"]
var readMethods = ["resume", "pause"]
var readEvents = ["data", "close"]
var slice = Array.prototype.slice
module.exports = duplex
function forEach (arr, fn) {
if (arr.forEach) {
return arr.forEach(fn)
}
for (var i = 0; i < arr.length; i++) {
fn(arr[i], i)
}
}
function duplex(writer, reader) {
var stream = new Stream()
var ended = false
forEach(writeMethods, proxyWriter)
forEach(readMethods, proxyReader)
forEach(readEvents, proxyStream)
reader.on("end", handleEnd)
writer.on("drain", function() {
stream.emit("drain")
})
writer.on("error", reemit)
reader.on("error", reemit)
stream.writable = writer.writable
stream.readable = reader.readable
return stream
function proxyWriter(methodName) {
stream[methodName] = method
function method() {
return writer[methodName].apply(writer, arguments)
}
}
function proxyReader(methodName) {
stream[methodName] = method
function method() {
stream.emit(methodName)
var func = reader[methodName]
if (func) {
return func.apply(reader, arguments)
}
reader.emit(methodName)
}
}
function proxyStream(methodName) {
reader.on(methodName, reemit)
function reemit() {
var args = slice.call(arguments)
args.unshift(methodName)
stream.emit.apply(stream, args)
}
}
function handleEnd() {
if (ended) {
return
}
ended = true
var args = slice.call(arguments)
args.unshift("end")
stream.emit.apply(stream, args)
}
function reemit(err) {
stream.emit("error", err)
}
}

View File

@@ -0,0 +1,47 @@
{
"name": "duplexer",
"version": "0.1.1",
"description": "Creates a duplex stream",
"keywords": [],
"author": "Raynos <raynos2@gmail.com>",
"repository": "git://github.com/Raynos/duplexer.git",
"main": "index",
"homepage": "https://github.com/Raynos/duplexer",
"contributors": [
{
"name": "Jake Verbaten"
}
],
"bugs": {
"url": "https://github.com/Raynos/duplexer/issues",
"email": "raynos2@gmail.com"
},
"devDependencies": {
"tape": "0.3.3",
"through": "~0.1.4"
},
"licenses": [
{
"type": "MIT",
"url": "http://github.com/Raynos/duplexer/raw/master/LICENSE"
}
],
"scripts": {
"test": "node test"
},
"testling": {
"files": "test/index.js",
"browsers": [
"ie/8..latest",
"firefox/16..latest",
"firefox/nightly",
"chrome/22..latest",
"chrome/canary",
"opera/12..latest",
"opera/next",
"safari/5.1..latest",
"ipad/6.0..latest",
"iphone/6.0..latest"
]
}
}

View File

@@ -0,0 +1,31 @@
var through = require("through")
var test = require("tape")
var duplex = require("../index")
var readable = through()
var writable = through(write)
var written = 0
var data = 0
var stream = duplex(writable, readable)
function write() {
written++
}
stream.on("data", ondata)
function ondata() {
data++
}
test("emit and write", function(t) {
t.plan(2)
stream.write()
readable.emit("data")
t.equal(written, 1, "should have written once")
t.equal(data, 1, "should have recived once")
})

View File

@@ -0,0 +1,3 @@
node_modules
node_modules/*
npm_debug.log

View File

@@ -0,0 +1,3 @@
language: node_js
node_js:
- "0.10"

View File

@@ -0,0 +1,24 @@
The MIT License (MIT)
Copyright (c) 2011 Dominic Tarr
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,25 @@
var inspect = require('util').inspect
if(!module.parent) {
var es = require('..') //load event-stream
es.pipe( //pipe joins streams together
process.openStdin(), //open stdin
es.split(), //split stream to break on newlines
es.map(function (data, callback) {//turn this async function into a stream
var j
try {
j = JSON.parse(data) //try to parse input into json
} catch (err) {
return callback(null, data) //if it fails just pass it anyway
}
callback(null, inspect(j)) //render it nicely
}),
process.stdout // pipe it to stdout !
)
}
// run this
//
// curl -sS registry.npmjs.org/event-stream | node pretty.js
//

View File

@@ -0,0 +1,324 @@
//filter will reemit the data if cb(err,pass) pass is truthy
// reduce is more tricky
// maybe we want to group the reductions or emit progress updates occasionally
// the most basic reduce just emits one 'data' event after it has recieved 'end'
var Stream = require('stream').Stream
, es = exports
, through = require('through')
, from = require('from')
, duplex = require('duplexer')
, map = require('map-stream')
, pause = require('pause-stream')
, split = require('split')
, pipeline = require('stream-combiner')
, immediately = global.setImmediate || process.nextTick;
es.Stream = Stream //re-export Stream from core
es.through = through
es.from = from
es.duplex = duplex
es.map = map
es.pause = pause
es.split = split
es.pipeline = es.connect = es.pipe = pipeline
// merge / concat
//
// combine multiple streams into a single stream.
// will emit end only once
es.concat = //actually this should be called concat
es.merge = function (/*streams...*/) {
var toMerge = [].slice.call(arguments)
if (toMerge.length === 1 && (toMerge[0] instanceof Array)) {
toMerge = toMerge[0] //handle array as arguments object
}
var stream = new Stream()
stream.setMaxListeners(0) // allow adding more than 11 streams
var endCount = 0
stream.writable = stream.readable = true
if (toMerge.length) {
toMerge.forEach(function (e) {
e.pipe(stream, {end: false})
var ended = false
e.on('end', function () {
if(ended) return
ended = true
endCount ++
if(endCount == toMerge.length)
stream.emit('end')
})
})
} else {
process.nextTick(function () {
stream.emit('end')
})
}
stream.write = function (data) {
this.emit('data', data)
}
stream.destroy = function () {
toMerge.forEach(function (e) {
if(e.destroy) e.destroy()
})
}
return stream
}
// writable stream, collects all events into an array
// and calls back when 'end' occurs
// mainly I'm using this to test the other functions
es.writeArray = function (done) {
if ('function' !== typeof done)
throw new Error('function writeArray (done): done must be function')
var a = new Stream ()
, array = [], isDone = false
a.write = function (l) {
array.push(l)
}
a.end = function () {
isDone = true
done(null, array)
}
a.writable = true
a.readable = false
a.destroy = function () {
a.writable = a.readable = false
if(isDone) return
done(new Error('destroyed before end'), array)
}
return a
}
//return a Stream that reads the properties of an object
//respecting pause() and resume()
es.readArray = function (array) {
var stream = new Stream()
, i = 0
, paused = false
, ended = false
stream.readable = true
stream.writable = false
if(!Array.isArray(array))
throw new Error('event-stream.read expects an array')
stream.resume = function () {
if(ended) return
paused = false
var l = array.length
while(i < l && !paused && !ended) {
stream.emit('data', array[i++])
}
if(i == l && !ended)
ended = true, stream.readable = false, stream.emit('end')
}
process.nextTick(stream.resume)
stream.pause = function () {
paused = true
}
stream.destroy = function () {
ended = true
stream.emit('close')
}
return stream
}
//
// readable (asyncFunction)
// return a stream that calls an async function while the stream is not paused.
//
// the function must take: (count, callback) {...
//
es.readable =
function (func, continueOnError) {
var stream = new Stream()
, i = 0
, paused = false
, ended = false
, reading = false
stream.readable = true
stream.writable = false
if('function' !== typeof func)
throw new Error('event-stream.readable expects async function')
stream.on('end', function () { ended = true })
function get (err, data) {
if(err) {
stream.emit('error', err)
if(!continueOnError) stream.emit('end')
} else if (arguments.length > 1)
stream.emit('data', data)
immediately(function () {
if(ended || paused || reading) return
try {
reading = true
func.call(stream, i++, function () {
reading = false
get.apply(null, arguments)
})
} catch (err) {
stream.emit('error', err)
}
})
}
stream.resume = function () {
paused = false
get()
}
process.nextTick(get)
stream.pause = function () {
paused = true
}
stream.destroy = function () {
stream.emit('end')
stream.emit('close')
ended = true
}
return stream
}
//
// map sync
//
es.mapSync = function (sync) {
return es.through(function write(data) {
var mappedData
try {
mappedData = sync(data)
} catch (err) {
return this.emit('error', err)
}
if (mappedData !== undefined)
this.emit('data', mappedData)
})
}
//
// log just print out what is coming through the stream, for debugging
//
es.log = function (name) {
return es.through(function (data) {
var args = [].slice.call(arguments)
if(name) console.error(name, data)
else console.error(data)
this.emit('data', data)
})
}
//
// child -- pipe through a child process
//
es.child = function (child) {
return es.duplex(child.stdin, child.stdout)
}
//
// parse
//
// must be used after es.split() to ensure that each chunk represents a line
// source.pipe(es.split()).pipe(es.parse())
es.parse = function (options) {
var emitError = !!(options ? options.error : false)
return es.through(function (data) {
var obj
try {
if(data) //ignore empty lines
obj = JSON.parse(data.toString())
} catch (err) {
if (emitError)
return this.emit('error', err)
return console.error(err, 'attempting to parse:', data)
}
//ignore lines that where only whitespace.
if(obj !== undefined)
this.emit('data', obj)
})
}
//
// stringify
//
es.stringify = function () {
var Buffer = require('buffer').Buffer
return es.mapSync(function (e){
return JSON.stringify(Buffer.isBuffer(e) ? e.toString() : e) + '\n'
})
}
//
// replace a string within a stream.
//
// warn: just concatenates the string and then does str.split().join().
// probably not optimal.
// for smallish responses, who cares?
// I need this for shadow-npm so it's only relatively small json files.
es.replace = function (from, to) {
return es.pipeline(es.split(from), es.join(to))
}
//
// join chunks with a joiner. just like Array#join
// also accepts a callback that is passed the chunks appended together
// this is still supported for legacy reasons.
//
es.join = function (str) {
//legacy api
if('function' === typeof str)
return es.wait(str)
var first = true
return es.through(function (data) {
if(!first)
this.emit('data', str)
first = false
this.emit('data', data)
return true
})
}
//
// wait. callback when 'end' is emitted, with all chunks appended as string.
//
es.wait = function (callback) {
var arr = []
return es.through(function (data) { arr.push(data) },
function () {
var body = Buffer.isBuffer(arr[0]) ? Buffer.concat(arr)
: arr.join('')
this.emit('data', body)
this.emit('end')
if(callback) callback(null, body)
})
}
es.pipeable = function () {
throw new Error('[EVENT-STREAM] es.pipeable is deprecated')
}

View File

@@ -0,0 +1,54 @@
{
"name": "event-stream",
"version": "3.3.4",
"description": "construct pipes of streams of events",
"homepage": "http://github.com/dominictarr/event-stream",
"repository": {
"type": "git",
"url": "git://github.com/dominictarr/event-stream.git"
},
"dependencies": {
"through": "~2.3.1",
"duplexer": "~0.1.1",
"from": "~0",
"map-stream": "~0.1.0",
"pause-stream": "0.0.11",
"split": "0.3",
"stream-combiner": "~0.0.4"
},
"devDependencies": {
"asynct": "*",
"it-is": "1",
"ubelt": "~3.2.2",
"stream-spec": "~0.3.5",
"tape": "~2.3.0"
},
"scripts": {
"prepublish": "npm ls && npm test",
"test": "asynct test/",
"test_tap": "set -e; for t in test/*.js; do node $t; done"
},
"testling": {
"files": "test/*.js",
"browsers": {
"ie": [
8,
9
],
"firefox": [
13
],
"chrome": [
20
],
"safari": [
5.1
],
"opera": [
12
]
}
},
"license": "MIT",
"author": "Dominic Tarr <dominic.tarr@gmail.com> (http://bit.ly/dominictarr)"
}

View File

@@ -0,0 +1,314 @@
# EventStream
<img src=https://secure.travis-ci.org/dominictarr/event-stream.png?branch=master>
[![browser status](http://ci.testling.com/dominictarr/event-stream.png)]
(http://ci.testling.com/dominictarr/event-stream)
[Streams](http://nodejs.org/api/stream.html "Stream") are node's best and most misunderstood idea, and
_<em>EventStream</em>_ is a toolkit to make creating and working with streams <em>easy</em>.
Normally, streams are only used for IO,
but in event stream we send all kinds of objects down the pipe.
If your application's <em>input</em> and <em>output</em> are streams,
shouldn't the <em>throughput</em> be a stream too?
The *EventStream* functions resemble the array functions,
because Streams are like Arrays, but laid out in time, rather than in memory.
<em>All the `event-stream` functions return instances of `Stream`</em>.
`event-stream` creates
[0.8 streams](https://github.com/joyent/node/blob/v0.8/doc/api/stream.markdown)
, which are compatible with [0.10 streams](http://nodejs.org/api/stream.html "Stream").
>NOTE: I shall use the term <em>"through stream"</em> to refer to a stream that is writable <em>and</em> readable.
### [simple example](https://github.com/dominictarr/event-stream/blob/master/examples/pretty.js):
``` js
//pretty.js
if(!module.parent) {
var es = require('event-stream')
var inspect = require('util').inspect
process.stdin //connect streams together with `pipe`
.pipe(es.split()) //split stream to break on newlines
.pipe(es.map(function (data, cb) { //turn this async function into a stream
cb(null
, inspect(JSON.parse(data))) //render it nicely
}))
.pipe(process.stdout) // pipe it to stdout !
}
```
run it ...
``` bash
curl -sS registry.npmjs.org/event-stream | node pretty.js
```
[node Stream documentation](http://nodejs.org/api/stream.html)
## through (write?, end?)
Re-emits data synchronously. Easy way to create synchronous through streams.
Pass in optional `write` and `end` methods. They will be called in the
context of the stream. Use `this.pause()` and `this.resume()` to manage flow.
Check `this.paused` to see current flow state. (write always returns `!this.paused`)
this function is the basis for most of the synchronous streams in `event-stream`.
``` js
es.through(function write(data) {
this.emit('data', data)
//this.pause()
},
function end () { //optional
this.emit('end')
})
```
## map (asyncFunction)
Create a through stream from an asynchronous function.
``` js
var es = require('event-stream')
es.map(function (data, callback) {
//transform data
// ...
callback(null, data)
})
```
Each map MUST call the callback. It may callback with data, with an error or with no arguments,
* `callback()` drop this data.
this makes the map work like `filter`,
note:`callback(null,null)` is not the same, and will emit `null`
* `callback(null, newData)` turn data into newData
* `callback(error)` emit an error for this item.
>Note: if a callback is not called, `map` will think that it is still being processed,
>every call must be answered or the stream will not know when to end.
>
>Also, if the callback is called more than once, every call but the first will be ignored.
## mapSync (syncFunction)
Same as `map`, but the callback is called synchronously. Based on `es.through`
## split (matcher)
Break up a stream and reassemble it so that each line is a chunk. matcher may be a `String`, or a `RegExp`
Example, read every line in a file ...
``` js
fs.createReadStream(file, {flags: 'r'})
.pipe(es.split())
.pipe(es.map(function (line, cb) {
//do something with the line
cb(null, line)
}))
```
`split` takes the same arguments as `string.split` except it defaults to '\n' instead of ',', and the optional `limit` parameter is ignored.
[String#split](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/split)
## join (separator)
Create a through stream that emits `separator` between each chunk, just like Array#join.
(for legacy reasons, if you pass a callback instead of a string, join is a synonym for `es.wait`)
## merge (stream1,...,streamN) or merge (streamArray)
> concat → merge
Merges streams into one and returns it.
Incoming data will be emitted as soon it comes into - no ordering will be applied (for example: `data1 data1 data2 data1 data2` - where `data1` and `data2` is data from two streams).
Counts how many streams were passed to it and emits end only when all streams emitted end.
```js
es.merge(
process.stdout,
process.stderr
).pipe(fs.createWriteStream('output.log'));
```
It can also take an Array of streams as input like this:
```js
es.merge([
fs.createReadStream('input1.txt'),
fs.createReadStream('input2.txt')
]).pipe(fs.createWriteStream('output.log'));
```
## replace (from, to)
Replace all occurrences of `from` with `to`. `from` may be a `String` or a `RegExp`.
Works just like `string.split(from).join(to)`, but streaming.
## parse
Convenience function for parsing JSON chunks. For newline separated JSON,
use with `es.split`. By default it logs parsing errors by `console.error`;
for another behaviour, transforms created by `es.parse({error: true})` will
emit error events for exceptions thrown from `JSON.parse`, unmodified.
``` js
fs.createReadStream(filename)
.pipe(es.split()) //defaults to lines.
.pipe(es.parse())
```
## stringify
convert javascript objects into lines of text. The text will have whitespace escaped and have a `\n` appended, so it will be compatible with `es.parse`
``` js
objectStream
.pipe(es.stringify())
.pipe(fs.createWriteStream(filename))
```
## readable (asyncFunction)
create a readable stream (that respects pause) from an async function.
while the stream is not paused,
the function will be polled with `(count, callback)`,
and `this` will be the readable stream.
``` js
es.readable(function (count, callback) {
if(streamHasEnded)
return this.emit('end')
//...
this.emit('data', data) //use this way to emit multiple chunks per call.
callback() // you MUST always call the callback eventually.
// the function will not be called again until you do this.
})
```
you can also pass the data and the error to the callback.
you may only call the callback once.
calling the same callback more than once will have no effect.
## readArray (array)
Create a readable stream from an Array.
Just emit each item as a data event, respecting `pause` and `resume`.
``` js
var es = require('event-stream')
, reader = es.readArray([1,2,3])
reader.pipe(...)
```
If you want the stream behave like a 0.10 stream you will need to wrap it using [`Readable.wrap()`](http://nodejs.org/api/stream.html#stream_readable_wrap_stream) function. Example:
``` js
var s = new stream.Readable({objectMode: true}).wrap(es.readArray([1,2,3]));
```
## writeArray (callback)
create a writeable stream from a callback,
all `data` events are stored in an array, which is passed to the callback when the stream ends.
``` js
var es = require('event-stream')
, reader = es.readArray([1, 2, 3])
, writer = es.writeArray(function (err, array){
//array deepEqual [1, 2, 3]
})
reader.pipe(writer)
```
## pause ()
A stream that buffers all chunks when paused.
``` js
var ps = es.pause()
ps.pause() //buffer the stream, also do not allow 'end'
ps.resume() //allow chunks through
```
## duplex (writeStream, readStream)
Takes a writable stream and a readable stream and makes them appear as a readable writable stream.
It is assumed that the two streams are connected to each other in some way.
(This is used by `pipeline` and `child`.)
``` js
var grep = cp.exec('grep Stream')
es.duplex(grep.stdin, grep.stdout)
```
## child (child_process)
Create a through stream from a child process ...
``` js
var cp = require('child_process')
es.child(cp.exec('grep Stream')) // a through stream
```
## wait (callback)
waits for stream to emit 'end'.
joins chunks of a stream into a single string or buffer.
takes an optional callback, which will be passed the
complete string/buffer when it receives the 'end' event.
also, emits a single 'data' event.
``` js
readStream.pipe(es.wait(function (err, body) {
// have complete text here.
}))
```
# Other Stream Modules
These modules are not included as a part of *EventStream* but may be
useful when working with streams.
## [reduce (syncFunction, initial)](https://github.com/parshap/node-stream-reduce)
Like `Array.prototype.reduce` but for streams. Given a sync reduce
function and an initial value it will return a through stream that emits
a single data event with the reduced value once the input stream ends.
``` js
var reduce = require("stream-reduce");
process.stdin.pipe(reduce(function(acc, data) {
return acc + data.length;
}, 0)).on("data", function(length) {
console.log("stdin size:", length);
});
```

View File

@@ -0,0 +1,86 @@
var es = require('../')
, it = require('it-is').style('colour')
, d = require('ubelt')
function makeExamplePipe() {
return es.connect(
es.map(function (data, callback) {
callback(null, data * 2)
}),
es.map(function (data, callback) {
d.delay(callback)(null, data)
}),
es.map(function (data, callback) {
callback(null, data + 2)
}))
}
exports['simple pipe'] = function (test) {
var pipe = makeExamplePipe()
pipe.on('data', function (data) {
it(data).equal(18)
test.done()
})
pipe.write(8)
}
exports['read array then map'] = function (test) {
var readThis = d.map(3, 6, 100, d.id) //array of multiples of 3 < 100
, first = es.readArray(readThis)
, read = []
, pipe =
es.connect(
first,
es.map(function (data, callback) {
callback(null, {data: data})
}),
es.map(function (data, callback) {
callback(null, {data: data})
}),
es.writeArray(function (err, array) {
it(array).deepEqual(d.map(readThis, function (data) {
return {data: {data: data}}
}))
test.done()
})
)
}
exports ['connect returns a stream'] = function (test) {
var rw =
es.connect(
es.map(function (data, callback) {
callback(null, data * 2)
}),
es.map(function (data, callback) {
callback(null, data * 5)
})
)
it(rw).has({readable: true, writable: true})
var array = [190, 24, 6, 7, 40, 57, 4, 6]
, _array = []
, c =
es.connect(
es.readArray(array),
rw,
es.log('after rw:'),
es.writeArray(function (err, _array) {
it(_array).deepEqual(array.map(function (e) { return e * 10 }))
test.done()
})
)
}
require('./helper')(module)

View File

@@ -0,0 +1,12 @@
var tape = require('tape')
module.exports = function (m) {
if(m.parent) return
for(var name in m.exports) {
tape(name, function (t) {
console.log('start', name)
t.done = t.end
m.exports[name](t)
})
}
}

View File

@@ -0,0 +1,29 @@
var es = require('../')
, it = require('it-is').style('colour')
, d = require('ubelt')
exports.merge = function (t) {
var odd = d.map(1, 3, 100, d.id) //array of multiples of 3 < 100
var even = d.map(2, 4, 100, d.id) //array of multiples of 3 < 100
var r1 = es.readArray(even)
var r2 = es.readArray(odd)
var endCount = 0
var writer = es.writeArray(function (err, array){
if(err) throw err //unpossible
it(array.sort()).deepEqual(even.concat(odd).sort())
if (++endCount === 2) t.done()
})
var writer2 = es.writeArray(function (err, array){
if(err) throw err //unpossible
it(array.sort()).deepEqual(even.concat(odd).sort())
if (++endCount === 2) t.done()
})
es.merge(r1, r2).pipe(writer)
es.merge([r1, r2]).pipe(writer2)
}
require('./helper')(module)

View File

@@ -0,0 +1,32 @@
var es = require('../')
, it = require('it-is').style('colour')
exports ['es.parse() writes parsing errors with console.error'] = function (test) {
var parseStream = es.parse()
var oldConsoleError = console.error
console.error = function () {
console.error = oldConsoleError
it(arguments.length > 0).ok()
test.done()
}
// bare word is not valid JSON
parseStream.write('A')
}
exports ['es.parse({error: true(thy)}) emits error events from parsing'] = function (test) {
var parseStream = es.parse({error: 1})
var expectedError
try {
JSON.parse('A')
} catch(e) {
expectedError = e
}
parseStream.on('error', function (e) {
it(e).deepEqual(expectedError)
process.nextTick(function () {
test.done()
})
}).write('A')
}

View File

@@ -0,0 +1,39 @@
var es = require('../')
, it = require('it-is')
, d = require('ubelt')
exports ['gate buffers when shut'] = function (test) {
var hundy = d.map(1,100, d.id)
, gate = es.pause()
, ten = 10
es.connect(
es.readArray(hundy),
es.log('after readArray'),
gate,
//es.log('after gate'),
es.map(function (num, next) {
//stick a map in here to check that gate never emits when open
it(gate.paused).equal(false)
console.log('data', num)
if(!--ten) {
console.log('PAUSE')
gate.pause()//.resume()
d.delay(gate.resume.bind(gate), 10)()
ten = 10
}
next(null, num)
}),
es.writeArray(function (err, array) { //just realized that I should remove the error param. errors will be emitted
console.log('eonuhoenuoecbulc')
it(array).deepEqual(hundy)
test.done()
})
)
gate.resume()
}
require('./helper')(module)

View File

@@ -0,0 +1,52 @@
var es = require('..')
exports['do not duplicate errors'] = function (test) {
var errors = 0;
var pipe = es.pipeline(
es.through(function(data) {
return this.emit('data', data);
}),
es.through(function(data) {
return this.emit('error', new Error(data));
})
)
pipe.on('error', function(err) {
errors++
console.log('error count', errors)
process.nextTick(function () {
return test.done();
})
})
return pipe.write('meh');
}
exports['3 pipe do not duplicate errors'] = function (test) {
var errors = 0;
var pipe = es.pipeline(
es.through(function(data) {
return this.emit('data', data);
}),
es.through(function(data) {
return this.emit('error', new Error(data));
}),
es.through()
)
pipe.on('error', function(err) {
errors++
console.log('error count', errors)
process.nextTick(function () {
return test.done();
})
})
return pipe.write('meh');
}
require('./helper')(module)

View File

@@ -0,0 +1,89 @@
var es = require('../')
, it = require('it-is').style('colour')
, d = require('ubelt')
function readStream(stream, pauseAt, done) {
if(!done) done = pauseAt, pauseAt = -1
var array = []
stream.on('data', function (data) {
array.push(data)
if(!--pauseAt )
stream.pause(), done(null, array)
})
stream.on('error', done)
stream.on('end', function (data) {
done(null, array)
})
}
exports ['read an array'] = function (test) {
var readThis = d.map(3, 6, 100, d.id) //array of multiples of 3 < 100
var reader = es.readArray(readThis)
var writer = es.writeArray(function (err, array){
if(err) throw err //unpossible
it(array).deepEqual(readThis)
test.done()
})
reader.pipe(writer)
}
exports ['read an array and pause it.'] = function (test) {
var readThis = d.map(3, 6, 100, d.id) //array of multiples of 3 < 100
var reader = es.readArray(readThis)
readStream(reader, 10, function (err, data) {
if(err) throw err
it(data).deepEqual([3, 6, 9, 12, 15, 18, 21, 24, 27, 30])
readStream(reader, 10, function (err, data) {
it(data).deepEqual([33, 36, 39, 42, 45, 48, 51, 54, 57, 60])
test.done()
})
reader.resume()
})
}
exports ['reader is readable, but not writeable'] = function (test) {
var reader = es.readArray([1])
it(reader).has({
readable: true,
writable: false
})
test.done()
}
exports ['read one item per tick'] = function (test) {
var readThis = d.map(3, 6, 100, d.id) //array of multiples of 3 < 100
var drains = 0
var reader = es.readArray(readThis)
var tickMapper = es.map(function (data,callback) {
process.nextTick(function () {
callback(null, data)
})
//since tickMapper is returning false
//pipe should pause the writer until a drain occurs
return false
})
reader.pipe(tickMapper)
readStream(tickMapper, function (err, array) {
it(array).deepEqual(readThis)
it(array.length).deepEqual(readThis.length)
it(drains).equal(readThis.length)
test.done()
})
tickMapper.on('drain', function () {
drains ++
})
}
require('./helper')(module)

View File

@@ -0,0 +1,197 @@
var es = require('../')
, it = require('it-is').style('colour')
, u = require('ubelt')
exports ['read an array'] = function (test) {
console.log('readable')
return test.end()
var readThis = u.map(3, 6, 100, u.id) //array of multiples of 3 < 100
console.log('readable')
var reader =
es.readable(function (i, callback) {
if(i >= readThis.length)
return this.emit('end')
console.log('readable')
callback(null, readThis[i])
})
var writer = es.writeArray(function (err, array){
if(err) throw err
it(array).deepEqual(readThis)
test.done()
})
reader.pipe(writer)
}
exports ['read an array - async'] = function (test) {
var readThis = u.map(3, 6, 100, u.id) //array of multiples of 3 < 100
var reader =
es.readable(function (i, callback) {
if(i >= readThis.length)
return this.emit('end')
u.delay(callback)(null, readThis[i])
})
var writer = es.writeArray(function (err, array){
if(err) throw err
it(array).deepEqual(readThis)
test.done()
})
reader.pipe(writer)
}
exports ['emit data then call next() also works'] = function (test) {
var readThis = u.map(3, 6, 100, u.id) //array of multiples of 3 < 100
var reader =
es.readable(function (i, next) {
if(i >= readThis.length)
return this.emit('end')
this.emit('data', readThis[i])
next()
})
var writer = es.writeArray(function (err, array){
if(err) throw err
it(array).deepEqual(readThis)
test.done()
})
reader.pipe(writer)
}
exports ['callback emits error, then stops'] = function (test) {
var err = new Error('INTENSIONAL ERROR')
, called = 0
var reader =
es.readable(function (i, callback) {
if(called++)
return
callback(err)
})
reader.on('error', function (_err){
it(_err).deepEqual(err)
u.delay(function() {
it(called).equal(1)
test.done()
}, 50)()
})
}
exports['readable does not call read concurrently'] = function (test) {
var current = 0
var source = es.readable(function(count, cb){
current ++
if(count > 100)
return this.emit('end')
u.delay(function(){
current --
it(current).equal(0)
cb(null, {ok: true, n: count});
})();
});
var destination = es.map(function(data, cb){
//console.info(data);
cb();
});
var all = es.connect(source, destination);
destination.on('end', test.done)
}
exports ['does not raise a warning: Recursive process.nextTick detected'] = function (test) {
var readThisDelayed;
u.delay(function () {
readThisDelayed = [1, 3, 5];
})();
es.readable(function (count, callback) {
if (readThisDelayed) {
var that = this;
readThisDelayed.forEach(function (item) {
that.emit('data', item);
});
this.emit('end');
test.done();
}
callback();
});
};
//
// emitting multiple errors is not supported by stream.
//
// I do not think that this is a good idea, at least, there should be an option to pipe to
// continue on error. it makes alot ef sense, if you are using Stream like I am, to be able to emit multiple errors.
// an error might not necessarily mean the end of the stream. it depends on the error, at least.
//
// I will start a thread on the mailing list. I'd rather that than use a custom `pipe` implementation.
//
// basically, I want to be able use pipe to transform objects, and if one object is invalid,
// the next might still be good, so I should get to choose if it's gonna stop.
// re-enstate this test when this issue progresses.
//
// hmm. I could add this to es.connect by deregistering the error listener,
// but I would rather it be an option in core.
/*
exports ['emit multiple errors, with 2nd parameter (continueOnError)'] = function (test) {
var readThis = d.map(1, 100, d.id)
, errors = 0
var reader =
es.readable(function (i, callback) {
console.log(i, readThis.length)
if(i >= readThis.length)
return this.emit('end')
if(!(readThis[i] % 7))
return callback(readThis[i])
callback(null, readThis[i])
}, true)
var writer = es.writeArray(function (err, array) {
if(err) throw err
it(array).every(function (u){
it(u % 7).notEqual(0)
}).property('length', 80)
it(errors).equal(14)
test.done()
})
reader.on('error', function (u) {
errors ++
console.log(u)
if('number' !== typeof u)
throw u
it(u % 7).equal(0)
})
reader.pipe(writer)
}
*/
require('./helper')(module)

View File

@@ -0,0 +1,76 @@
var es = require('../')
, it = require('it-is').style('colour')
, d = require('ubelt')
, spec = require('stream-spec')
var next = process.nextTick
var fizzbuzz = '12F4BF78FB11F1314FB1617F19BF2223FB26F2829FB3132F34BF3738FB41F4344FB4647F49BF5253FB56F5859FB6162F64BF6768FB71F7374FB7677F79BF8283FB86F8889FB9192F94BF9798FB'
, fizz7buzz = '12F4BFseven8FB11F1314FB161sevenF19BF2223FB26F2829FB3132F34BF3seven38FB41F4344FB464sevenF49BF5253FB56F5859FB6162F64BF6seven68FBseven1Fseven3seven4FBseven6sevensevenFseven9BF8283FB86F8889FB9192F94BF9seven98FB'
, fizzbuzzwhitespce = ' 12F4BF78FB11F1314FB1617F19BF2223FB26F2829FB3132F34BF3738FB41F4344FB4647F49BF5253FB56F5859FB6162F64BF6768FB71F7374FB7677F79BF8283FB86F8889FB9192F94BF9798FB '
exports ['fizz buzz'] = function (test) {
var readThis = d.map(1, 100, function (i) {
return (
! (i % 3 || i % 5) ? "FB" :
!(i % 3) ? "F" :
!(i % 5) ? "B" :
''+i
)
}) //array of multiples of 3 < 100
var reader = es.readArray(readThis)
var join = es.wait(function (err, string){
it(string).equal(fizzbuzz)
test.done()
})
reader.pipe(join)
}
exports ['fizz buzz replace'] = function (test) {
var split = es.split(/(1)/)
var replace = es.replace('7', 'seven')
// var x = spec(replace).through()
split
.pipe(replace)
.pipe(es.join(function (err, string) {
it(string).equal(fizz7buzz)
}))
replace.on('close', function () {
// x.validate()
test.done()
})
split.write(fizzbuzz)
split.end()
}
exports ['fizz buzz replace whitespace using regexp'] = function (test) {
var split = es.split(/(1)/)
var replaceLeading = es.replace(/^[\s]*/, '')
var replaceTrailing = es.replace(/[\s]*$/, '')
// var x = spec(replace).through()
split
.pipe(replaceLeading)
.pipe(replaceTrailing)
.pipe(es.join(function (err, string) {
it(string).equal(fizzbuzz)
}))
replaceTrailing.on('close', function () {
// x.validate()
test.done()
})
split.write(fizzbuzz)
split.end()
}
require('./helper')(module)

View File

@@ -0,0 +1,343 @@
'use strict';
var es = require('../')
, it = require('it-is')
, u = require('ubelt')
, spec = require('stream-spec')
, Stream = require('stream')
, from = require('from')
, through = require('through')
//REFACTOR THIS TEST TO USE es.readArray and es.writeArray
function writeArray(array, stream) {
array.forEach( function (j) {
stream.write(j)
})
stream.end()
}
function readStream(stream, done) {
var array = []
stream.on('data', function (data) {
array.push(data)
})
stream.on('error', done)
stream.on('end', function (data) {
done(null, array)
})
}
//call sink on each write,
//and complete when finished.
function pauseStream (prob, delay) {
var pauseIf = (
'number' == typeof prob
? function () {
return Math.random() < prob
}
: 'function' == typeof prob
? prob
: 0.1
)
var delayer = (
!delay
? process.nextTick
: 'number' == typeof delay
? function (next) { setTimeout(next, delay) }
: delay
)
return es.through(function (data) {
if(!this.paused && pauseIf()) {
console.log('PAUSE STREAM PAUSING')
this.pause()
var self = this
delayer(function () {
console.log('PAUSE STREAM RESUMING')
self.resume()
})
}
console.log("emit ('data', " + data + ')')
this.emit('data', data)
})
}
exports ['simple map'] = function (test) {
var input = u.map(1, 1000, function () {
return Math.random()
})
var expected = input.map(function (v) {
return v * 2
})
var pause = pauseStream(0.1)
var fs = from(input)
var ts = es.writeArray(function (err, ar) {
it(ar).deepEqual(expected)
test.done()
})
var map = es.through(function (data) {
this.emit('data', data * 2)
})
spec(map).through().validateOnExit()
spec(pause).through().validateOnExit()
fs.pipe(map).pipe(pause).pipe(ts)
}
exports ['simple map applied to a stream'] = function (test) {
var input = [1,2,3,7,5,3,1,9,0,2,4,6]
//create event stream from
var doubler = es.map(function (data, cb) {
cb(null, data * 2)
})
spec(doubler).through().validateOnExit()
//a map is only a middle man, so it is both readable and writable
it(doubler).has({
readable: true,
writable: true,
})
readStream(doubler, function (err, output) {
it(output).deepEqual(input.map(function (j) {
return j * 2
}))
// process.nextTick(x.validate)
test.done()
})
writeArray(input, doubler)
}
exports['pipe two maps together'] = function (test) {
var input = [1,2,3,7,5,3,1,9,0,2,4,6]
//create event stream from
function dd (data, cb) {
cb(null, data * 2)
}
var doubler1 = es.map(dd), doubler2 = es.map(dd)
doubler1.pipe(doubler2)
spec(doubler1).through().validateOnExit()
spec(doubler2).through().validateOnExit()
readStream(doubler2, function (err, output) {
it(output).deepEqual(input.map(function (j) {
return j * 4
}))
test.done()
})
writeArray(input, doubler1)
}
//next:
//
// test pause, resume and drian.
//
// then make a pipe joiner:
//
// plumber (evStr1, evStr2, evStr3, evStr4, evStr5)
//
// will return a single stream that write goes to the first
exports ['map will not call end until the callback'] = function (test) {
var ticker = es.map(function (data, cb) {
process.nextTick(function () {
cb(null, data * 2)
})
})
spec(ticker).through().validateOnExit()
ticker.write('x')
ticker.end()
ticker.on('end', function () {
test.done()
})
}
exports ['emit error thrown'] = function (test) {
var err = new Error('INTENSIONAL ERROR')
, mapper =
es.map(function () {
throw err
})
mapper.on('error', function (_err) {
it(_err).equal(err)
test.done()
})
// onExit(spec(mapper).basic().validate)
//need spec that says stream may error.
mapper.write('hello')
}
exports ['emit error calledback'] = function (test) {
var err = new Error('INTENSIONAL ERROR')
, mapper =
es.map(function (data, callback) {
callback(err)
})
mapper.on('error', function (_err) {
it(_err).equal(err)
test.done()
})
mapper.write('hello')
}
exports ['do not emit drain if not paused'] = function (test) {
var map = es.map(function (data, callback) {
u.delay(callback)(null, 1)
return true
})
spec(map).through().pausable().validateOnExit()
map.on('drain', function () {
it(false).ok('should not emit drain unless the stream is paused')
})
it(map.write('hello')).equal(true)
it(map.write('hello')).equal(true)
it(map.write('hello')).equal(true)
setTimeout(function () {map.end()},10)
map.on('end', test.done)
}
exports ['emits drain if paused, when all '] = function (test) {
var active = 0
var drained = false
var map = es.map(function (data, callback) {
active ++
u.delay(function () {
active --
callback(null, 1)
})()
console.log('WRITE', false)
return false
})
spec(map).through().validateOnExit()
map.on('drain', function () {
drained = true
it(active).equal(0, 'should emit drain when all maps are done')
})
it(map.write('hello')).equal(false)
it(map.write('hello')).equal(false)
it(map.write('hello')).equal(false)
process.nextTick(function () {map.end()},10)
map.on('end', function () {
console.log('end')
it(drained).ok('shoud have emitted drain before end')
test.done()
})
}
exports ['map applied to a stream with filtering'] = function (test) {
var input = [1,2,3,7,5,3,1,9,0,2,4,6]
var doubler = es.map(function (data, callback) {
if (data % 2)
callback(null, data * 2)
else
callback()
})
readStream(doubler, function (err, output) {
it(output).deepEqual(input.filter(function (j) {
return j % 2
}).map(function (j) {
return j * 2
}))
test.done()
})
spec(doubler).through().validateOnExit()
writeArray(input, doubler)
}
exports ['simple mapSync applied to a stream'] = function (test) {
var input = [1,2,3,7,5,3,1,9,0,2,4,6]
var doubler = es.mapSync(function (data) {
return data * 2
})
readStream(doubler, function (err, output) {
it(output).deepEqual(input.map(function (j) {
return j * 2
}))
test.done()
})
spec(doubler).through().validateOnExit()
writeArray(input, doubler)
}
exports ['mapSync applied to a stream with filtering'] = function (test) {
var input = [1,2,3,7,5,3,1,9,0,2,4,6]
var doubler = es.mapSync(function (data) {
if (data % 2)
return data * 2
})
readStream(doubler, function (err, output) {
it(output).deepEqual(input.filter(function (j) {
return j % 2
}).map(function (j) {
return j * 2
}))
test.done()
})
spec(doubler).through().validateOnExit()
writeArray(input, doubler)
}
require('./helper')(module)

View File

@@ -0,0 +1,86 @@
/*
assert that data is called many times
assert that end is called eventually
assert that when stream enters pause state,
on drain is emitted eventually.
*/
var es = require('..')
var it = require('it-is').style('colour')
var spec = require('stream-spec')
exports['simple stream'] = function (test) {
var stream = es.through()
var x = spec(stream).basic().pausable()
stream.write(1)
stream.write(1)
stream.pause()
stream.write(1)
stream.resume()
stream.write(1)
stream.end(2) //this will call write()
process.nextTick(function (){
x.validate()
test.done()
})
}
exports['throw on write when !writable'] = function (test) {
var stream = es.through()
var x = spec(stream).basic().pausable()
stream.write(1)
stream.write(1)
stream.end(2) //this will call write()
stream.write(1) //this will be throwing..., but the spec will catch it.
process.nextTick(function () {
x.validate()
test.done()
})
}
exports['end fast'] = function (test) {
var stream = es.through()
var x = spec(stream).basic().pausable()
stream.end() //this will call write()
process.nextTick(function () {
x.validate()
test.done()
})
}
/*
okay, that was easy enough, whats next?
say, after you call paused(), write should return false
until resume is called.
simple way to implement this:
write must return !paused
after pause() paused = true
after resume() paused = false
on resume, if !paused drain is emitted again.
after drain, !paused
there are lots of subtle ordering bugs in streams.
example, set !paused before emitting drain.
the stream api is stateful.
*/
require('./helper')(module)

View File

@@ -0,0 +1,47 @@
var es = require('../')
, it = require('it-is').style('colour')
, d = require('ubelt')
, join = require('path').join
, fs = require('fs')
, Stream = require('stream').Stream
, spec = require('stream-spec')
exports ['es.split() works like String#split'] = function (test) {
var readme = join(__filename)
, expected = fs.readFileSync(readme, 'utf-8').split('\n')
, cs = es.split()
, actual = []
, ended = false
, x = spec(cs).through()
var a = new Stream ()
a.write = function (l) {
actual.push(l.trim())
}
a.end = function () {
ended = true
expected.forEach(function (v,k) {
//String.split will append an empty string ''
//if the string ends in a split pattern.
//es.split doesn't which was breaking this test.
//clearly, appending the empty string is correct.
//tests are passing though. which is the current job.
if(v)
it(actual[k]).like(v)
})
//give the stream time to close
process.nextTick(function () {
test.done()
x.validate()
})
}
a.writable = true
fs.createReadStream(readme, {flags: 'r'}).pipe(cs)
cs.pipe(a)
}
require('./helper')(module)

View File

@@ -0,0 +1,15 @@
var es = require('../')
exports['handle buffer'] = function (t) {
es.stringify().on('data', function (d) {
t.equal(d.trim(), JSON.stringify('HELLO'))
t.end()
}).write(new Buffer('HELLO'))
}
require('./helper')(module)

View File

@@ -0,0 +1,31 @@
var es = require('../')
, it = require('it-is').style('colour')
, d = require('ubelt')
exports ['write an array'] = function (test) {
var readThis = d.map(3, 6, 100, d.id) //array of multiples of 3 < 100
var writer = es.writeArray(function (err, array){
if(err) throw err //unpossible
it(array).deepEqual(readThis)
test.done()
})
d.each(readThis, writer.write.bind(writer))
writer.end()
}
exports ['writer is writable, but not readable'] = function (test) {
var reader = es.writeArray(function () {})
it(reader).has({
readable: false,
writable: true
})
test.done()
}
require('./helper')(module)

View File

@@ -0,0 +1 @@
node_modules

View File

@@ -0,0 +1,6 @@
language: node_js
node_js:
- "node"
- "6"
- "5"
- "4"

View File

@@ -0,0 +1,15 @@
Apache License, Version 2.0
Copyright (c) 2011 Dominic Tarr
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,24 @@
The MIT License
Copyright (c) 2011 Dominic Tarr
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,68 @@
'use strict';
var Stream = require('stream')
// from
//
// a stream that reads from an source.
// source may be an array, or a function.
// from handles pause behaviour for you.
module.exports =
function from (source) {
if(Array.isArray(source)) {
var source_index = 0, source_len = source.length;
return from (function (i) {
if(source_index < source_len)
this.emit('data', source[source_index++])
else
this.emit('end')
return true
})
}
var s = new Stream(), i = 0
s.ended = false
s.started = false
s.readable = true
s.writable = false
s.paused = false
s.ended = false
s.pause = function () {
s.started = true
s.paused = true
}
function next () {
s.started = true
if(s.ended) return
while(!s.ended && !s.paused && source.call(s, i++, function () {
if(!s.ended && !s.paused)
process.nextTick(next);
}))
;
}
s.resume = function () {
s.started = true
s.paused = false
next()
}
s.on('end', function () {
s.ended = true
s.readable = false
process.nextTick(s.destroy)
})
s.destroy = function () {
s.ended = true
s.emit('close')
}
/*
by default, the stream will start emitting at nextTick
if you want, you can pause it, after pipeing.
you can also resume before next tick, and that will also
work.
*/
process.nextTick(function () {
if(!s.started) s.resume()
})
return s
}

View File

@@ -0,0 +1,26 @@
{
"name": "from",
"version": "0.1.7",
"description": "Easy way to make a Readable Stream",
"main": "index.js",
"scripts": {
"test": "asynct test/*.js"
},
"repository": {
"type": "git",
"url": "git://github.com/dominictarr/from.git"
},
"keywords": [
"stream",
"streams",
"readable",
"easy"
],
"devDependencies": {
"asynct": "1",
"stream-spec": "0",
"assertions": "~2.3.0"
},
"author": "Dominic Tarr <dominic.tarr@gmail.com> (dominictarr.com)",
"license": "MIT"
}

View File

@@ -0,0 +1,40 @@
[![TravisCI Build Status](https://travis-ci.org/nmhnmh/from.svg?branch=master)](https://travis-ci.org/nmhnmh/from)
# from
An easy way to create a `readable Stream`.
## from(function getChunk(count, next))
from takes a `getChunk` function and returns a stream.
`getChunk` is called again and again, after each time the user calls `next()`,
until the user emits `'end'`
if `pause()` is called, the `getChunk` won't be called again untill `resume()` is called.
```js
var from = require('from')
var stream =
from(function getChunk(count, next) {
//do some sort of data
this.emit('data', whatever)
if(itsOver)
this.emit('end')
//ready to handle the next chunk
next()
//or, if it's sync:
return true
})
```
## from(array)
from also takes an `Array` whose elements it emits one after another.
## License
MIT / Apache2

View File

@@ -0,0 +1,210 @@
var from = require('..')
var spec = require('stream-spec')
var a = require('assertions')
function read(stream, callback) {
var actual = []
stream.on('data', function (data) {
actual.push(data)
})
stream.once('end', function () {
callback(null, actual)
})
stream.once('error', function (err) {
callback(err)
})
}
function pause(stream) {
stream.on('data', function () {
if(Math.random() > 0.1) return
stream.pause()
process.nextTick(function () {
stream.resume()
})
})
}
exports['inc'] = function (test) {
var fs = from(function (i) {
this.emit('data', i)
if(i >= 99)
return this.emit('end')
return true
})
spec(fs).readable().validateOnExit()
read(fs, function (err, arr) {
test.equal(arr.length, 100)
test.done()
})
}
exports['inc - async'] = function (test) {
var fs = from(function (i, next) {
this.emit('data', i)
if(i >= 99)
return this.emit('end')
next();
})
spec(fs).readable().validateOnExit()
read(fs, function (err, arr) {
test.equal(arr.length, 100)
test.done()
})
}
exports['large stream - from an array'] = function (test) {
var l = 100000
, expected = []
while(l--) expected.push(l * Math.random())
var fs = from(expected.slice())
spec(fs).readable().validateOnExit()
read(fs, function (err, arr) {
a.deepEqual(arr, expected)
test.done()
})
}
exports['large stream - callback return true'] = function (test) {
var fs = from(function (i, next) {
this.emit('data', i)
if(i >= 99999)
return this.emit('end')
return true;
})
spec(fs).readable().validateOnExit()
read(fs, function (err, arr) {
test.equal(arr.length, 100000)
test.done()
})
}
exports['large stream - callback call next()'] = function (test) {
var fs = from(function (i, next) {
this.emit('data', i)
if(i >= 99999)
return this.emit('end')
next();
})
spec(fs).readable().validateOnExit()
read(fs, function (err, arr) {
test.equal(arr.length, 100000)
test.done()
})
}
exports['simple'] = function (test) {
var l = 1000
, expected = []
while(l--) expected.push(l * Math.random())
var t = from(expected.slice())
spec(t)
.readable()
.pausable({strict: true})
.validateOnExit()
read(t, function (err, actual) {
if(err) test.error(err) //fail
a.deepEqual(actual, expected)
test.done()
})
}
exports['simple pausable'] = function (test) {
var l = 1000
, expected = []
while(l--) expected.push(l * Math.random())
var t = from(expected.slice())
spec(t)
.readable()
.pausable({strict: true})
.validateOnExit()
pause(t)
read(t, function (err, actual) {
if(err) test.error(err) //fail
a.deepEqual(actual, expected)
test.done()
})
}
exports['simple (not strictly pausable) setTimeout'] = function (test) {
var l = 10
, expected = []
while(l--) expected.push(l * Math.random())
var _expected = expected.slice()
var t = from(function (i, n) {
var self = this
setTimeout(function () {
if(_expected.length)
self.emit('data', _expected.shift())
else
if(!self.ended)
self.emit('end')
n()
}, 3)
})
/*
using from in this way will not be strictly pausable.
it could be extended to buffer outputs, but I think a better
way would be to use a PauseStream that implements strict pause.
*/
spec(t)
.readable()
.pausable({strict: false })
.validateOnExit()
//pause(t)
var paused = false
var i = setInterval(function () {
if(!paused) t.pause()
else t.resume()
paused = !paused
}, 2)
t.on('end', function () {
clearInterval(i)
})
read(t, function (err, actual) {
if(err) test.error(err) //fail
a.deepEqual(actual, expected)
test.done()
})
}

View File

@@ -0,0 +1,4 @@
language: node_js
node_js:
- 0.8
- 0.9

View File

@@ -0,0 +1,19 @@
Copyright (c) 2012 Colingo.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,30 @@
# global
<!-- [![build status][1]][2]
[![browser support][3]][4] -->
Require global variables
## Example
```js
var global = require("global")
var document = require("global/document")
var window = require("global/window")
```
## Installation
`npm install global`
## Contributors
- Raynos
## MIT Licenced
[1]: https://secure.travis-ci.org/Colingo/global.png
[2]: http://travis-ci.org/Colingo/global
[3]: http://ci.testling.com/Colingo/global.png
[4]: http://ci.testling.com/Colingo/global

View File

@@ -0,0 +1 @@
module.exports = console;

View File

@@ -0,0 +1,17 @@
var topLevel = typeof global !== 'undefined' ? global :
typeof window !== 'undefined' ? window : {}
var minDoc = require('min-document');
var doccy;
if (typeof document !== 'undefined') {
doccy = document;
} else {
doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];
if (!doccy) {
doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
}
}
module.exports = doccy;

View File

@@ -0,0 +1,63 @@
{
"name": "global",
"version": "4.4.0",
"description": "Require global variables",
"keywords": [],
"author": "Raynos <raynos2@gmail.com>",
"repository": "git://github.com/Raynos/global.git",
"main": "window.js",
"homepage": "https://github.com/Raynos/global",
"contributors": [
{
"name": "Raynos"
}
],
"bugs": {
"url": "https://github.com/Raynos/global/issues",
"email": "raynos2@gmail.com"
},
"browser": {
"min-document": false,
"individual": false
},
"dependencies": {
"min-document": "^2.19.0",
"process": "^0.11.10"
},
"devDependencies": {
"tape": "^2.12.0"
},
"license": "MIT",
"scripts": {
"test": "node ./test",
"build": "browserify test/index.js -o test/static/bundle.js",
"testem": "testem"
},
"testling": {
"files": "test/index.js",
"browsers": {
"ie": [
"8",
"9",
"10"
],
"firefox": [
"16",
"17",
"nightly"
],
"chrome": [
"22",
"23",
"canary"
],
"opera": [
"12",
"next"
],
"safari": [
"5.1"
]
}
}
}

View File

@@ -0,0 +1 @@
module.exports = require('process');

View File

@@ -0,0 +1,13 @@
var win;
if (typeof window !== "undefined") {
win = window;
} else if (typeof global !== "undefined") {
win = global;
} else if (typeof self !== "undefined"){
win = self;
} else {
win = {};
}
module.exports = win;

View File

@@ -0,0 +1,15 @@
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -0,0 +1,166 @@
# lru cache
A cache object that deletes the least-recently-used items.
[![Build Status](https://travis-ci.org/isaacs/node-lru-cache.svg?branch=master)](https://travis-ci.org/isaacs/node-lru-cache) [![Coverage Status](https://coveralls.io/repos/isaacs/node-lru-cache/badge.svg?service=github)](https://coveralls.io/github/isaacs/node-lru-cache)
## Installation:
```javascript
npm install lru-cache --save
```
## Usage:
```javascript
var LRU = require("lru-cache")
, options = { max: 500
, length: function (n, key) { return n * 2 + key.length }
, dispose: function (key, n) { n.close() }
, maxAge: 1000 * 60 * 60 }
, cache = new LRU(options)
, otherCache = new LRU(50) // sets just the max size
cache.set("key", "value")
cache.get("key") // "value"
// non-string keys ARE fully supported
// but note that it must be THE SAME object, not
// just a JSON-equivalent object.
var someObject = { a: 1 }
cache.set(someObject, 'a value')
// Object keys are not toString()-ed
cache.set('[object Object]', 'a different value')
assert.equal(cache.get(someObject), 'a value')
// A similar object with same keys/values won't work,
// because it's a different object identity
assert.equal(cache.get({ a: 1 }), undefined)
cache.reset() // empty the cache
```
If you put more stuff in it, then items will fall out.
If you try to put an oversized thing in it, then it'll fall out right
away.
## Options
* `max` The maximum size of the cache, checked by applying the length
function to all values in the cache. Not setting this is kind of
silly, since that's the whole purpose of this lib, but it defaults
to `Infinity`. Setting it to a non-number or negative number will
throw a `TypeError`. Setting it to 0 makes it be `Infinity`.
* `maxAge` Maximum age in ms. Items are not pro-actively pruned out
as they age, but if you try to get an item that is too old, it'll
drop it and return undefined instead of giving it to you.
Setting this to a negative value will make everything seem old!
Setting it to a non-number will throw a `TypeError`.
* `length` Function that is used to calculate the length of stored
items. If you're storing strings or buffers, then you probably want
to do something like `function(n, key){return n.length}`. The default is
`function(){return 1}`, which is fine if you want to store `max`
like-sized things. The item is passed as the first argument, and
the key is passed as the second argumnet.
* `dispose` Function that is called on items when they are dropped
from the cache. This can be handy if you want to close file
descriptors or do other cleanup tasks when items are no longer
accessible. Called with `key, value`. It's called *before*
actually removing the item from the internal cache, so if you want
to immediately put it back in, you'll have to do that in a
`nextTick` or `setTimeout` callback or it won't do anything.
* `stale` By default, if you set a `maxAge`, it'll only actually pull
stale items out of the cache when you `get(key)`. (That is, it's
not pre-emptively doing a `setTimeout` or anything.) If you set
`stale:true`, it'll return the stale value before deleting it. If
you don't set this, then it'll return `undefined` when you try to
get a stale entry, as if it had already been deleted.
* `noDisposeOnSet` By default, if you set a `dispose()` method, then
it'll be called whenever a `set()` operation overwrites an existing
key. If you set this option, `dispose()` will only be called when a
key falls out of the cache, not when it is overwritten.
* `updateAgeOnGet` When using time-expiring entries with `maxAge`,
setting this to `true` will make each item's effective time update
to the current time whenever it is retrieved from cache, causing it
to not expire. (It can still fall out of cache based on recency of
use, of course.)
## API
* `set(key, value, maxAge)`
* `get(key) => value`
Both of these will update the "recently used"-ness of the key.
They do what you think. `maxAge` is optional and overrides the
cache `maxAge` option if provided.
If the key is not found, `get()` will return `undefined`.
The key and val can be any value.
* `peek(key)`
Returns the key value (or `undefined` if not found) without
updating the "recently used"-ness of the key.
(If you find yourself using this a lot, you *might* be using the
wrong sort of data structure, but there are some use cases where
it's handy.)
* `del(key)`
Deletes a key out of the cache.
* `reset()`
Clear the cache entirely, throwing away all values.
* `has(key)`
Check if a key is in the cache, without updating the recent-ness
or deleting it for being stale.
* `forEach(function(value,key,cache), [thisp])`
Just like `Array.prototype.forEach`. Iterates over all the keys
in the cache, in order of recent-ness. (Ie, more recently used
items are iterated over first.)
* `rforEach(function(value,key,cache), [thisp])`
The same as `cache.forEach(...)` but items are iterated over in
reverse order. (ie, less recently used items are iterated over
first.)
* `keys()`
Return an array of the keys in the cache.
* `values()`
Return an array of the values in the cache.
* `length`
Return total length of objects in cache taking into account
`length` options function.
* `itemCount`
Return total quantity of objects currently in cache. Note, that
`stale` (see options) items are returned as part of this item
count.
* `dump()`
Return an array of the cache entries ready for serialization and usage
with 'destinationCache.load(arr)`.
* `load(cacheEntriesArray)`
Loads another cache entries array, obtained with `sourceCache.dump()`,
into the cache. The destination cache is reset before loading new entries
* `prune()`
Manually iterates over the entire cache proactively pruning old entries

View File

@@ -0,0 +1,334 @@
'use strict'
// A linked list to keep track of recently-used-ness
const Yallist = require('yallist')
const MAX = Symbol('max')
const LENGTH = Symbol('length')
const LENGTH_CALCULATOR = Symbol('lengthCalculator')
const ALLOW_STALE = Symbol('allowStale')
const MAX_AGE = Symbol('maxAge')
const DISPOSE = Symbol('dispose')
const NO_DISPOSE_ON_SET = Symbol('noDisposeOnSet')
const LRU_LIST = Symbol('lruList')
const CACHE = Symbol('cache')
const UPDATE_AGE_ON_GET = Symbol('updateAgeOnGet')
const naiveLength = () => 1
// lruList is a yallist where the head is the youngest
// item, and the tail is the oldest. the list contains the Hit
// objects as the entries.
// Each Hit object has a reference to its Yallist.Node. This
// never changes.
//
// cache is a Map (or PseudoMap) that matches the keys to
// the Yallist.Node object.
class LRUCache {
constructor (options) {
if (typeof options === 'number')
options = { max: options }
if (!options)
options = {}
if (options.max && (typeof options.max !== 'number' || options.max < 0))
throw new TypeError('max must be a non-negative number')
// Kind of weird to have a default max of Infinity, but oh well.
const max = this[MAX] = options.max || Infinity
const lc = options.length || naiveLength
this[LENGTH_CALCULATOR] = (typeof lc !== 'function') ? naiveLength : lc
this[ALLOW_STALE] = options.stale || false
if (options.maxAge && typeof options.maxAge !== 'number')
throw new TypeError('maxAge must be a number')
this[MAX_AGE] = options.maxAge || 0
this[DISPOSE] = options.dispose
this[NO_DISPOSE_ON_SET] = options.noDisposeOnSet || false
this[UPDATE_AGE_ON_GET] = options.updateAgeOnGet || false
this.reset()
}
// resize the cache when the max changes.
set max (mL) {
if (typeof mL !== 'number' || mL < 0)
throw new TypeError('max must be a non-negative number')
this[MAX] = mL || Infinity
trim(this)
}
get max () {
return this[MAX]
}
set allowStale (allowStale) {
this[ALLOW_STALE] = !!allowStale
}
get allowStale () {
return this[ALLOW_STALE]
}
set maxAge (mA) {
if (typeof mA !== 'number')
throw new TypeError('maxAge must be a non-negative number')
this[MAX_AGE] = mA
trim(this)
}
get maxAge () {
return this[MAX_AGE]
}
// resize the cache when the lengthCalculator changes.
set lengthCalculator (lC) {
if (typeof lC !== 'function')
lC = naiveLength
if (lC !== this[LENGTH_CALCULATOR]) {
this[LENGTH_CALCULATOR] = lC
this[LENGTH] = 0
this[LRU_LIST].forEach(hit => {
hit.length = this[LENGTH_CALCULATOR](hit.value, hit.key)
this[LENGTH] += hit.length
})
}
trim(this)
}
get lengthCalculator () { return this[LENGTH_CALCULATOR] }
get length () { return this[LENGTH] }
get itemCount () { return this[LRU_LIST].length }
rforEach (fn, thisp) {
thisp = thisp || this
for (let walker = this[LRU_LIST].tail; walker !== null;) {
const prev = walker.prev
forEachStep(this, fn, walker, thisp)
walker = prev
}
}
forEach (fn, thisp) {
thisp = thisp || this
for (let walker = this[LRU_LIST].head; walker !== null;) {
const next = walker.next
forEachStep(this, fn, walker, thisp)
walker = next
}
}
keys () {
return this[LRU_LIST].toArray().map(k => k.key)
}
values () {
return this[LRU_LIST].toArray().map(k => k.value)
}
reset () {
if (this[DISPOSE] &&
this[LRU_LIST] &&
this[LRU_LIST].length) {
this[LRU_LIST].forEach(hit => this[DISPOSE](hit.key, hit.value))
}
this[CACHE] = new Map() // hash of items by key
this[LRU_LIST] = new Yallist() // list of items in order of use recency
this[LENGTH] = 0 // length of items in the list
}
dump () {
return this[LRU_LIST].map(hit =>
isStale(this, hit) ? false : {
k: hit.key,
v: hit.value,
e: hit.now + (hit.maxAge || 0)
}).toArray().filter(h => h)
}
dumpLru () {
return this[LRU_LIST]
}
set (key, value, maxAge) {
maxAge = maxAge || this[MAX_AGE]
if (maxAge && typeof maxAge !== 'number')
throw new TypeError('maxAge must be a number')
const now = maxAge ? Date.now() : 0
const len = this[LENGTH_CALCULATOR](value, key)
if (this[CACHE].has(key)) {
if (len > this[MAX]) {
del(this, this[CACHE].get(key))
return false
}
const node = this[CACHE].get(key)
const item = node.value
// dispose of the old one before overwriting
// split out into 2 ifs for better coverage tracking
if (this[DISPOSE]) {
if (!this[NO_DISPOSE_ON_SET])
this[DISPOSE](key, item.value)
}
item.now = now
item.maxAge = maxAge
item.value = value
this[LENGTH] += len - item.length
item.length = len
this.get(key)
trim(this)
return true
}
const hit = new Entry(key, value, len, now, maxAge)
// oversized objects fall out of cache automatically.
if (hit.length > this[MAX]) {
if (this[DISPOSE])
this[DISPOSE](key, value)
return false
}
this[LENGTH] += hit.length
this[LRU_LIST].unshift(hit)
this[CACHE].set(key, this[LRU_LIST].head)
trim(this)
return true
}
has (key) {
if (!this[CACHE].has(key)) return false
const hit = this[CACHE].get(key).value
return !isStale(this, hit)
}
get (key) {
return get(this, key, true)
}
peek (key) {
return get(this, key, false)
}
pop () {
const node = this[LRU_LIST].tail
if (!node)
return null
del(this, node)
return node.value
}
del (key) {
del(this, this[CACHE].get(key))
}
load (arr) {
// reset the cache
this.reset()
const now = Date.now()
// A previous serialized cache has the most recent items first
for (let l = arr.length - 1; l >= 0; l--) {
const hit = arr[l]
const expiresAt = hit.e || 0
if (expiresAt === 0)
// the item was created without expiration in a non aged cache
this.set(hit.k, hit.v)
else {
const maxAge = expiresAt - now
// dont add already expired items
if (maxAge > 0) {
this.set(hit.k, hit.v, maxAge)
}
}
}
}
prune () {
this[CACHE].forEach((value, key) => get(this, key, false))
}
}
const get = (self, key, doUse) => {
const node = self[CACHE].get(key)
if (node) {
const hit = node.value
if (isStale(self, hit)) {
del(self, node)
if (!self[ALLOW_STALE])
return undefined
} else {
if (doUse) {
if (self[UPDATE_AGE_ON_GET])
node.value.now = Date.now()
self[LRU_LIST].unshiftNode(node)
}
}
return hit.value
}
}
const isStale = (self, hit) => {
if (!hit || (!hit.maxAge && !self[MAX_AGE]))
return false
const diff = Date.now() - hit.now
return hit.maxAge ? diff > hit.maxAge
: self[MAX_AGE] && (diff > self[MAX_AGE])
}
const trim = self => {
if (self[LENGTH] > self[MAX]) {
for (let walker = self[LRU_LIST].tail;
self[LENGTH] > self[MAX] && walker !== null;) {
// We know that we're about to delete this one, and also
// what the next least recently used key will be, so just
// go ahead and set it now.
const prev = walker.prev
del(self, walker)
walker = prev
}
}
}
const del = (self, node) => {
if (node) {
const hit = node.value
if (self[DISPOSE])
self[DISPOSE](hit.key, hit.value)
self[LENGTH] -= hit.length
self[CACHE].delete(hit.key)
self[LRU_LIST].removeNode(node)
}
}
class Entry {
constructor (key, value, length, now, maxAge) {
this.key = key
this.value = value
this.length = length
this.now = now
this.maxAge = maxAge || 0
}
}
const forEachStep = (self, fn, node, thisp) => {
let hit = node.value
if (isStale(self, hit)) {
del(self, node)
if (!self[ALLOW_STALE])
hit = undefined
}
if (hit)
fn.call(thisp, hit.value, hit.key, self)
}
module.exports = LRUCache

View File

@@ -0,0 +1,34 @@
{
"name": "lru-cache",
"description": "A cache object that deletes the least-recently-used items.",
"version": "6.0.0",
"author": "Isaac Z. Schlueter <i@izs.me>",
"keywords": [
"mru",
"lru",
"cache"
],
"scripts": {
"test": "tap",
"snap": "tap",
"preversion": "npm test",
"postversion": "npm publish",
"prepublishOnly": "git push origin --follow-tags"
},
"main": "index.js",
"repository": "git://github.com/isaacs/node-lru-cache.git",
"devDependencies": {
"benchmark": "^2.1.4",
"tap": "^14.10.7"
},
"license": "ISC",
"dependencies": {
"yallist": "^4.0.0"
},
"files": [
"index.js"
],
"engines": {
"node": ">=10"
}
}

View File

@@ -0,0 +1,3 @@
node_modules
node_modules/*
npm_debug.log

View File

@@ -0,0 +1,4 @@
language: node_js
node_js:
- 0.6
- 0.8

View File

@@ -0,0 +1,22 @@
Copyright (c) 2011 Dominic Tarr
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,26 @@
var inspect = require('util').inspect
if(!module.parent) {
var map = require('..') //load map-stream
var es = require('event-stream') //load event-stream
es.pipe( //pipe joins streams together
process.openStdin(), //open stdin
es.split(), //split stream to break on newlines
map(function (data, callback) { //turn this async function into a stream
var j
try {
j = JSON.parse(data) //try to parse input into json
} catch (err) {
return callback(null, data) //if it fails just pass it anyway
}
callback(null, inspect(j)) //render it nicely
}),
process.stdout // pipe it to stdout !
)
}
// run this
//
// curl -sS registry.npmjs.org/event-stream | node pretty.js
//

View File

@@ -0,0 +1,145 @@
//filter will reemit the data if cb(err,pass) pass is truthy
// reduce is more tricky
// maybe we want to group the reductions or emit progress updates occasionally
// the most basic reduce just emits one 'data' event after it has recieved 'end'
var Stream = require('stream').Stream
//create an event stream and apply function to each .write
//emitting each response as data
//unless it's an empty callback
module.exports = function (mapper, opts) {
var stream = new Stream()
, self = this
, inputs = 0
, outputs = 0
, ended = false
, paused = false
, destroyed = false
, lastWritten = 0
, inNext = false
this.opts = opts || {};
var errorEventName = this.opts.failures ? 'failure' : 'error';
// Items that are not ready to be written yet (because they would come out of
// order) get stuck in a queue for later.
var writeQueue = {}
stream.writable = true
stream.readable = true
function queueData (data, number) {
var nextToWrite = lastWritten + 1
if (number === nextToWrite) {
// If it's next, and its not undefined write it
if (data !== undefined) {
stream.emit.apply(stream, ['data', data])
}
lastWritten ++
nextToWrite ++
} else {
// Otherwise queue it for later.
writeQueue[number] = data
}
// If the next value is in the queue, write it
if (writeQueue.hasOwnProperty(nextToWrite)) {
var dataToWrite = writeQueue[nextToWrite]
delete writeQueue[nextToWrite]
return queueData(dataToWrite, nextToWrite)
}
outputs ++
if(inputs === outputs) {
if(paused) paused = false, stream.emit('drain') //written all the incoming events
if(ended) end()
}
}
function next (err, data, number) {
if(destroyed) return
inNext = true
if (!err || self.opts.failures) {
queueData(data, number)
}
if (err) {
stream.emit.apply(stream, [ errorEventName, err ]);
}
inNext = false;
}
// Wrap the mapper function by calling its callback with the order number of
// the item in the stream.
function wrappedMapper (input, number, callback) {
return mapper.call(null, input, function(err, data){
callback(err, data, number)
})
}
stream.write = function (data) {
if(ended) throw new Error('map stream is not writable')
inNext = false
inputs ++
try {
//catch sync errors and handle them like async errors
var written = wrappedMapper(data, inputs, next)
paused = (written === false)
return !paused
} catch (err) {
//if the callback has been called syncronously, and the error
//has occured in an listener, throw it again.
if(inNext)
throw err
next(err)
return !paused
}
}
function end (data) {
//if end was called with args, write it,
ended = true //write will emit 'end' if ended is true
stream.writable = false
if(data !== undefined) {
return queueData(data, inputs)
} else if (inputs == outputs) { //wait for processing
stream.readable = false, stream.emit('end'), stream.destroy()
}
}
stream.end = function (data) {
if(ended) return
end()
}
stream.destroy = function () {
ended = destroyed = true
stream.writable = stream.readable = paused = false
process.nextTick(function () {
stream.emit('close')
})
}
stream.pause = function () {
paused = true
}
stream.resume = function () {
paused = false
}
return stream
}

View File

@@ -0,0 +1,24 @@
{
"name": "map-stream",
"version": "0.1.0",
"description": "construct pipes of streams of events",
"homepage": "http://github.com/dominictarr/map-stream",
"repository": {
"type": "git",
"url": "git://github.com/dominictarr/map-stream.git"
},
"dependencies": {
},
"devDependencies": {
"asynct": "*",
"it-is": "1",
"ubelt": "~2.9",
"stream-spec": "~0.2",
"event-stream": "~2.1",
"from": "0.0.2"
},
"scripts": {
"test": "asynct test/"
},
"author": "Dominic Tarr <dominic.tarr@gmail.com> (http://dominictarr.com)"
}

View File

@@ -0,0 +1,37 @@
# MapStream
Refactored out of [event-stream](https://github.com/dominictarr/event-stream)
##map (asyncFunction[, options])
Create a through stream from an asyncronous function.
``` js
var map = require('map-stream')
map(function (data, callback) {
//transform data
// ...
callback(null, data)
})
```
Each map MUST call the callback. It may callback with data, with an error or with no arguments,
* `callback()` drop this data.
this makes the map work like `filter`,
note:`callback(null,null)` is not the same, and will emit `null`
* `callback(null, newData)` turn data into newData
* `callback(error)` emit an error for this item.
>Note: if a callback is not called, `map` will think that it is still being processed,
>every call must be answered or the stream will not know when to end.
>
>Also, if the callback is called more than once, every call but the first will be ignored.
##Options
* `failures` - `boolean` continue mapping even if error occured. On error `map-stream` will emit `failure` event. (default: `false`)

View File

@@ -0,0 +1,318 @@
'use strict';
var map = require('../')
, it = require('it-is')
, u = require('ubelt')
, spec = require('stream-spec')
, from = require('from')
, Stream = require('stream')
, es = require('event-stream')
//REFACTOR THIS TEST TO USE es.readArray and es.writeArray
function writeArray(array, stream) {
array.forEach( function (j) {
stream.write(j)
})
stream.end()
}
function readStream(stream, done) {
var array = []
stream.on('data', function (data) {
array.push(data)
})
stream.on('error', done)
stream.on('end', function (data) {
done(null, array)
})
}
//call sink on each write,
//and complete when finished.
function pauseStream (prob, delay) {
var pauseIf = (
'number' == typeof prob
? function () {
return Math.random() < prob
}
: 'function' == typeof prob
? prob
: 0.1
)
var delayer = (
!delay
? process.nextTick
: 'number' == typeof delay
? function (next) { setTimeout(next, delay) }
: delay
)
return es.through(function (data) {
if(!this.paused && pauseIf()) {
console.log('PAUSE STREAM PAUSING')
this.pause()
var self = this
delayer(function () {
console.log('PAUSE STREAM RESUMING')
self.resume()
})
}
console.log("emit ('data', " + data + ')')
this.emit('data', data)
})
}
exports ['simple map applied to a stream'] = function (test) {
var input = [1,2,3,7,5,3,1,9,0,2,4,6]
//create event stream from
var doubler = map(function (data, cb) {
cb(null, data * 2)
})
spec(doubler).through().validateOnExit()
//a map is only a middle man, so it is both readable and writable
it(doubler).has({
readable: true,
writable: true,
})
readStream(doubler, function (err, output) {
it(output).deepEqual(input.map(function (j) {
return j * 2
}))
// process.nextTick(x.validate)
test.done()
})
writeArray(input, doubler)
}
exports ['stream comes back in the correct order'] = function (test) {
var input = [3, 2, 1]
var delayer = map(function(data, cb){
setTimeout(function () {
cb(null, data)
}, 100 * data)
})
readStream(delayer, function (err, output) {
it(output).deepEqual(input)
test.done()
})
writeArray(input, delayer)
}
exports ['continues on error event with failures `true`'] = function (test) {
var input = [1, 2, 3]
var delayer = map(function(data, cb){
cb(new Error('Something gone wrong'), data)
}, { failures: true })
readStream(delayer, function (err, output) {
it(output).deepEqual(input)
test.done()
})
writeArray(input, delayer)
}
exports['pipe two maps together'] = function (test) {
var input = [1,2,3,7,5,3,1,9,0,2,4,6]
//create event stream from
function dd (data, cb) {
cb(null, data * 2)
}
var doubler1 = map(dd), doubler2 = map(dd)
doubler1.pipe(doubler2)
spec(doubler1).through().validateOnExit()
spec(doubler2).through().validateOnExit()
readStream(doubler2, function (err, output) {
it(output).deepEqual(input.map(function (j) {
return j * 4
}))
test.done()
})
writeArray(input, doubler1)
}
//next:
//
// test pause, resume and drian.
//
// then make a pipe joiner:
//
// plumber (evStr1, evStr2, evStr3, evStr4, evStr5)
//
// will return a single stream that write goes to the first
exports ['map will not call end until the callback'] = function (test) {
var ticker = map(function (data, cb) {
process.nextTick(function () {
cb(null, data * 2)
})
})
spec(ticker).through().validateOnExit()
ticker.write('x')
ticker.end()
ticker.on('end', function () {
test.done()
})
}
exports ['emit failures with opts.failures === `ture`'] = function (test) {
var err = new Error('INTENSIONAL ERROR')
, mapper =
map(function () {
throw err
}, { failures: true })
mapper.on('failure', function (_err) {
it(_err).equal(err)
test.done()
})
mapper.write('hello')
}
exports ['emit error thrown'] = function (test) {
var err = new Error('INTENSIONAL ERROR')
, mapper =
map(function () {
throw err
})
mapper.on('error', function (_err) {
it(_err).equal(err)
test.done()
})
mapper.write('hello')
}
exports ['emit error calledback'] = function (test) {
var err = new Error('INTENSIONAL ERROR')
, mapper =
map(function (data, callback) {
callback(err)
})
mapper.on('error', function (_err) {
it(_err).equal(err)
test.done()
})
mapper.write('hello')
}
exports ['do not emit drain if not paused'] = function (test) {
var maps = map(function (data, callback) {
u.delay(callback)(null, 1)
return true
})
spec(maps).through().pausable().validateOnExit()
maps.on('drain', function () {
it(false).ok('should not emit drain unless the stream is paused')
})
it(maps.write('hello')).equal(true)
it(maps.write('hello')).equal(true)
it(maps.write('hello')).equal(true)
setTimeout(function () {maps.end()},10)
maps.on('end', test.done)
}
exports ['emits drain if paused, when all '] = function (test) {
var active = 0
var drained = false
var maps = map(function (data, callback) {
active ++
u.delay(function () {
active --
callback(null, 1)
})()
console.log('WRITE', false)
return false
})
spec(maps).through().validateOnExit()
maps.on('drain', function () {
drained = true
it(active).equal(0, 'should emit drain when all maps are done')
})
it(maps.write('hello')).equal(false)
it(maps.write('hello')).equal(false)
it(maps.write('hello')).equal(false)
process.nextTick(function () {maps.end()},10)
maps.on('end', function () {
console.log('end')
it(drained).ok('shoud have emitted drain before end')
test.done()
})
}
exports ['map applied to a stream with filtering'] = function (test) {
var input = [1,2,3,7,5,3,1,9,0,2,4,6]
var doubler = map(function (data, callback) {
if (data % 2)
callback(null, data * 2)
else
callback()
})
readStream(doubler, function (err, output) {
it(output).deepEqual(input.filter(function (j) {
return j % 2
}).map(function (j) {
return j * 2
}))
test.done()
})
spec(doubler).through().validateOnExit()
writeArray(input, doubler)
}

View File

@@ -0,0 +1,44 @@
# License information
## Contribution License Agreement
If you contribute code to this project, you are implicitly allowing your code
to be distributed under the MIT license. You are also implicitly verifying that
all code is your original work. `</legalese>`
## Marked
Copyright (c) 2018+, MarkedJS (https://github.com/markedjs/)
Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
## Markdown
Copyright © 2004, John Gruber
http://daringfireball.net/
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name “Markdown” nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
This software is provided by the copyright holders and contributors “as is” and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.

View File

@@ -0,0 +1,87 @@
<a href="https://marked.js.org">
<img width="60px" height="60px" src="https://marked.js.org/img/logo-black.svg" align="right" />
</a>
# Marked
[![npm](https://badgen.net/npm/v/marked)](https://www.npmjs.com/package/marked)
[![gzip size](https://badgen.net/badgesize/gzip/https://cdn.jsdelivr.net/npm/marked/marked.min.js)](https://cdn.jsdelivr.net/npm/marked/marked.min.js)
[![install size](https://badgen.net/packagephobia/install/marked)](https://packagephobia.now.sh/result?p=marked)
[![downloads](https://badgen.net/npm/dt/marked)](https://www.npmjs.com/package/marked)
[![github actions](https://github.com/markedjs/marked/workflows/Tests/badge.svg)](https://github.com/markedjs/marked/actions)
[![snyk](https://snyk.io/test/npm/marked/badge.svg)](https://snyk.io/test/npm/marked)
- ⚡ built for speed
- ⬇️ low-level compiler for parsing markdown without caching or blocking for long periods of time
- ⚖️ light-weight while implementing all markdown features from the supported flavors & specifications
- 🌐 works in a browser, on a server, or from a command line interface (CLI)
## Demo
Checkout the [demo page](https://marked.js.org/demo/) to see marked in action ⛹️
## Docs
Our [documentation pages](https://marked.js.org) are also rendered using marked 💯
Also read about:
* [Options](https://marked.js.org/#/USING_ADVANCED.md)
* [Extensibility](https://marked.js.org/#/USING_PRO.md)
## Compatibility
**Node.js:** Only [current and LTS](https://nodejs.org/en/about/releases/) Node.js versions are supported. End of life Node.js versions may become incompatible with Marked at any point in time.
**Browser:** Not IE11 :)
## Installation
**CLI:** `npm install -g marked`
**In-browser:** `npm install marked`
## Usage
### Warning: 🚨 Marked does not [sanitize](https://marked.js.org/#/USING_ADVANCED.md#options) the output HTML. Please use a sanitize library, like [DOMPurify](https://github.com/cure53/DOMPurify) (recommended), [sanitize-html](https://github.com/apostrophecms/sanitize-html) or [insane](https://github.com/bevacqua/insane) on the output HTML! 🚨
**CLI**
``` bash
# Example with stdin input
$ marked -o hello.html
hello world
^D
$ cat hello.html
<p>hello world</p>
```
```bash
# Print all options
$ marked --help
```
**Browser**
```html
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Marked in the browser</title>
</head>
<body>
<div id="content"></div>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script>
document.getElementById('content').innerHTML =
marked.parse('# Marked in the browser\n\nRendered by **marked**.');
</script>
</body>
</html>
```
## License
Copyright (c) 2011-2022, Christopher Jeffrey. (MIT License)

View File

@@ -0,0 +1,214 @@
#!/usr/bin/env node
/**
* Marked CLI
* Copyright (c) 2011-2013, Christopher Jeffrey (MIT License)
*/
import { promises } from 'fs';
import { marked } from '../lib/marked.esm.js';
const { readFile, writeFile } = promises;
/**
* Man Page
*/
async function help() {
const { spawn } = await import('child_process');
const options = {
cwd: process.cwd(),
env: process.env,
setsid: false,
stdio: 'inherit'
};
const { dirname, resolve } = await import('path');
const { fileURLToPath } = await import('url');
const __dirname = dirname(fileURLToPath(import.meta.url));
const helpText = await readFile(resolve(__dirname, '../man/marked.1.txt'), 'utf8');
// eslint-disable-next-line promise/param-names
await new Promise(res => {
spawn('man', [resolve(__dirname, '../man/marked.1')], options)
.on('error', () => {
console.log(helpText);
})
.on('close', res);
});
}
async function version() {
const { createRequire } = await import('module');
const require = createRequire(import.meta.url);
const pkg = require('../package.json');
console.log(pkg.version);
}
/**
* Main
*/
async function main(argv) {
const files = [];
const options = {};
let input;
let output;
let string;
let arg;
let tokens;
let opt;
function getarg() {
let arg = argv.shift();
if (arg.indexOf('--') === 0) {
// e.g. --opt
arg = arg.split('=');
if (arg.length > 1) {
// e.g. --opt=val
argv.unshift(arg.slice(1).join('='));
}
arg = arg[0];
} else if (arg[0] === '-') {
if (arg.length > 2) {
// e.g. -abc
argv = arg.substring(1).split('').map(function(ch) {
return '-' + ch;
}).concat(argv);
arg = argv.shift();
} else {
// e.g. -a
}
} else {
// e.g. foo
}
return arg;
}
while (argv.length) {
arg = getarg();
switch (arg) {
case '-o':
case '--output':
output = argv.shift();
break;
case '-i':
case '--input':
input = argv.shift();
break;
case '-s':
case '--string':
string = argv.shift();
break;
case '-t':
case '--tokens':
tokens = true;
break;
case '-h':
case '--help':
return await help();
case '-v':
case '--version':
return await version();
default:
if (arg.indexOf('--') === 0) {
opt = camelize(arg.replace(/^--(no-)?/, ''));
if (!marked.defaults.hasOwnProperty(opt)) {
continue;
}
if (arg.indexOf('--no-') === 0) {
options[opt] = typeof marked.defaults[opt] !== 'boolean'
? null
: false;
} else {
options[opt] = typeof marked.defaults[opt] !== 'boolean'
? argv.shift()
: true;
}
} else {
files.push(arg);
}
break;
}
}
async function getData() {
if (!input) {
if (files.length <= 2) {
if (string) {
return string;
}
return await getStdin();
}
input = files.pop();
}
return await readFile(input, 'utf8');
}
const data = await getData();
const html = tokens
? JSON.stringify(marked.lexer(data, options), null, 2)
: marked(data, options);
if (output) {
return await writeFile(output, html);
}
process.stdout.write(html + '\n');
}
/**
* Helpers
*/
function getStdin() {
return new Promise((resolve, reject) => {
const stdin = process.stdin;
let buff = '';
stdin.setEncoding('utf8');
stdin.on('data', function(data) {
buff += data;
});
stdin.on('error', function(err) {
reject(err);
});
stdin.on('end', function() {
resolve(buff);
});
stdin.resume();
});
}
function camelize(text) {
return text.replace(/(\w)-(\w)/g, function(_, a, b) {
return a + b.toUpperCase();
});
}
function handleError(err) {
if (err.code === 'ENOENT') {
console.error('marked: output to ' + err.path + ': No such directory');
return process.exit(1);
}
throw err;
}
/**
* Expose / Entry Point
*/
process.title = 'marked';
main(process.argv.slice()).then(code => {
process.exit(code || 0);
}).catch(err => {
handleError(err);
});

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More