Merge branch 'master' into fix-content-type
This commit is contained in:
commit
6853d077e7
|
@ -0,0 +1,6 @@
|
||||||
|
npm-debug.log
|
||||||
|
node_modules
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
data
|
||||||
|
*.DS_Store
|
|
@ -0,0 +1,63 @@
|
||||||
|
FROM node:14.8.0-stretch
|
||||||
|
|
||||||
|
RUN mkdir -p /usr/src/app && \
|
||||||
|
chown node:node /usr/src/app
|
||||||
|
|
||||||
|
USER node:node
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
COPY --chown=node:node . .
|
||||||
|
|
||||||
|
RUN npm install && \
|
||||||
|
npm install redis@0.8.1 && \
|
||||||
|
npm install pg@4.1.1 && \
|
||||||
|
npm install memcached@2.2.2 && \
|
||||||
|
npm install aws-sdk@2.738.0 && \
|
||||||
|
npm install rethinkdbdash@2.3.31
|
||||||
|
|
||||||
|
ENV STORAGE_TYPE=memcached \
|
||||||
|
STORAGE_HOST=127.0.0.1 \
|
||||||
|
STORAGE_PORT=11211\
|
||||||
|
STORAGE_EXPIRE_SECONDS=2592000\
|
||||||
|
STORAGE_DB=2 \
|
||||||
|
STORAGE_AWS_BUCKET= \
|
||||||
|
STORAGE_AWS_REGION= \
|
||||||
|
STORAGE_USENAMER= \
|
||||||
|
STORAGE_PASSWORD= \
|
||||||
|
STORAGE_FILEPATH=
|
||||||
|
|
||||||
|
ENV LOGGING_LEVEL=verbose \
|
||||||
|
LOGGING_TYPE=Console \
|
||||||
|
LOGGING_COLORIZE=true
|
||||||
|
|
||||||
|
ENV HOST=0.0.0.0\
|
||||||
|
PORT=7777\
|
||||||
|
KEY_LENGTH=10\
|
||||||
|
MAX_LENGTH=400000\
|
||||||
|
STATIC_MAX_AGE=86400\
|
||||||
|
RECOMPRESS_STATIC_ASSETS=true
|
||||||
|
|
||||||
|
ENV KEYGENERATOR_TYPE=phonetic \
|
||||||
|
KEYGENERATOR_KEYSPACE=
|
||||||
|
|
||||||
|
ENV RATELIMITS_NORMAL_TOTAL_REQUESTS=500\
|
||||||
|
RATELIMITS_NORMAL_EVERY_MILLISECONDS=60000 \
|
||||||
|
RATELIMITS_WHITELIST_TOTAL_REQUESTS= \
|
||||||
|
RATELIMITS_WHITELIST_EVERY_MILLISECONDS= \
|
||||||
|
# comma separated list for the whitelisted \
|
||||||
|
RATELIMITS_WHITELIST=example1.whitelist,example2.whitelist \
|
||||||
|
\
|
||||||
|
RATELIMITS_BLACKLIST_TOTAL_REQUESTS= \
|
||||||
|
RATELIMITS_BLACKLIST_EVERY_MILLISECONDS= \
|
||||||
|
# comma separated list for the blacklisted \
|
||||||
|
RATELIMITS_BLACKLIST=example1.blacklist,example2.blacklist
|
||||||
|
ENV DOCUMENTS=about=./about.md
|
||||||
|
|
||||||
|
EXPOSE ${PORT}
|
||||||
|
STOPSIGNAL SIGINT
|
||||||
|
ENTRYPOINT [ "bash", "docker-entrypoint.sh" ]
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s \
|
||||||
|
--retries=3 CMD [ "curl" , "-f" "localhost:${PORT}", "||", "exit", "1"]
|
||||||
|
CMD ["npm", "start"]
|
120
README.md
120
README.md
|
@ -198,6 +198,126 @@ Also, you must create an `uploads` table, which will store all the data for uplo
|
||||||
|
|
||||||
You can optionally add the `user` and `password` properties to use a user system.
|
You can optionally add the `user` and `password` properties to use a user system.
|
||||||
|
|
||||||
|
### Amazon S3
|
||||||
|
|
||||||
|
To use [Amazon S3](https://aws.amazon.com/s3/) as a storage system, you must
|
||||||
|
install the `aws-sdk` package via npm:
|
||||||
|
|
||||||
|
`npm install aws-sdk`
|
||||||
|
|
||||||
|
Once you've done that, your config section should look like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "amazon-s3",
|
||||||
|
"bucket": "your-bucket-name",
|
||||||
|
"region": "us-east-1"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Authentication is handled automatically by the client. Check
|
||||||
|
[Amazon's documentation](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html)
|
||||||
|
for more information. You will need to grant your role these permissions to
|
||||||
|
your bucket:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Action": [
|
||||||
|
"s3:GetObject",
|
||||||
|
"s3:PutObject"
|
||||||
|
],
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Resource": "arn:aws:s3:::your-bucket-name-goes-here/*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
### Build image
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build --tag haste-server .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run container
|
||||||
|
|
||||||
|
For this example we will run haste-server, and connect it to a redis server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --name haste-server-container --env STORAGE_TYPE=redis --env STORAGE_HOST=redis-server --env STORAGE_PORT=6379 haste-server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use docker-compose example
|
||||||
|
|
||||||
|
There is an example `docker-compose.yml` which runs haste-server together with memcached
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose up
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
The docker image is configured using environmental variables as you can see in the example above.
|
||||||
|
|
||||||
|
Here is a list of all the environment variables
|
||||||
|
|
||||||
|
### Storage
|
||||||
|
|
||||||
|
| Name | Default value | Description |
|
||||||
|
| :--------------------: | :-----------: | :-----------------------------------------------------------------------------------------------------------: |
|
||||||
|
| STORAGE_TYPE | memcached | Type of storage . Accepted values: "memcached","redis","postgres","rethinkdb", "amazon-s3", and "file" |
|
||||||
|
| STORAGE_HOST | 127.0.0.1 | Storage host. Applicable for types: memcached, redis, postgres, and rethinkdb |
|
||||||
|
| STORAGE_PORT | 11211 | Port on the storage host. Applicable for types: memcached, redis, postgres, and rethinkdb |
|
||||||
|
| STORAGE_EXPIRE_SECONDS | 2592000 | Number of seconds to expire keys in. Applicable for types. Redis, postgres, memcached. `expire` option to the |
|
||||||
|
| STORAGE_DB | 2 | The name of the database. Applicable for redis, postgres, and rethinkdb |
|
||||||
|
| STORAGE_PASSWORD | | Password for database. Applicable for redis, postges, rethinkdb . |
|
||||||
|
| STORAGE_USERNAME | | Database username. Applicable for postgres, and rethinkdb |
|
||||||
|
| STORAGE_AWS_BUCKET | | Applicable for amazon-s3. This is the name of the S3 bucket |
|
||||||
|
| STORAGE_AWS_REGION | | Applicable for amazon-s3. The region in which the bucket is located |
|
||||||
|
| STORAGE_FILEPATH | | Path to file to save data to. Applicable for type file |
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
|
||||||
|
| Name | Default value | Description |
|
||||||
|
| :---------------: | :-----------: | :---------: |
|
||||||
|
| LOGGING_LEVEL | verbose | |
|
||||||
|
| LOGGING_TYPE= | Console |
|
||||||
|
| LOGGING_COLORIZE= | true |
|
||||||
|
|
||||||
|
### Basics
|
||||||
|
|
||||||
|
| Name | Default value | Description |
|
||||||
|
| :----------------------: | :--------------: | :---------------------------------------------------------------------------------------: |
|
||||||
|
| HOST | 0.0.0.0 | The hostname which the server answers on |
|
||||||
|
| PORT | 7777 | The port on which the server is running |
|
||||||
|
| KEY_LENGTH | 10 | the length of the keys to user |
|
||||||
|
| MAX_LENGTH | 400000 | maximum length of a paste |
|
||||||
|
| STATIC_MAX_AGE | 86400 | max age for static assets |
|
||||||
|
| RECOMPRESS_STATIC_ASSETS | true | whether or not to compile static js assets |
|
||||||
|
| KEYGENERATOR_TYPE | phonetic | Type of key generator. Acceptable values: "phonetic", or "random" |
|
||||||
|
| KEYGENERATOR_KEYSPACE | | keySpace argument is a string of acceptable characters |
|
||||||
|
| DOCUMENTS | about=./about.md | Comma separated list of static documents to serve. ex: \n about=./about.md,home=./home.md |
|
||||||
|
|
||||||
|
### Rate limits
|
||||||
|
|
||||||
|
| Name | Default value | Description |
|
||||||
|
| :----------------------------------: | :-----------------------------------: | :--------------------------------------------------------------------------------------: |
|
||||||
|
| RATELIMITS_NORMAL_TOTAL_REQUESTS | 500 | By default anyone uncategorized will be subject to 500 requests in the defined timespan. |
|
||||||
|
| RATELIMITS_NORMAL_EVERY_MILLISECONDS | 60000 | The timespan to allow the total requests for uncategorized users |
|
||||||
|
| RATELIMITS_WHITELIST_TOTAL_REQUESTS | | By default client names in the whitelist will not have their requests limited. |
|
||||||
|
| RATELIMITS_WHITELIST_EVERY_SECONDS | | By default client names in the whitelist will not have their requests limited. |
|
||||||
|
| RATELIMITS_WHITELIST | example1.whitelist,example2.whitelist | Comma separated list of the clients which are in the whitelist pool |
|
||||||
|
| RATELIMITS_BLACKLIST_TOTAL_REQUESTS | | By default client names in the blacklist will be subject to 0 requests per hours. |
|
||||||
|
| RATELIMITS_BLACKLIST_EVERY_SECONDS | | By default client names in the blacklist will be subject to 0 requests per hours |
|
||||||
|
| RATELIMITS_BLACKLIST | example1.blacklist,example2.blacklist | Comma separated list of the clients which are in the blacklistpool. |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Author
|
## Author
|
||||||
|
|
||||||
John Crepezzi <john.crepezzi@gmail.com>
|
John Crepezzi <john.crepezzi@gmail.com>
|
||||||
|
|
|
@ -33,10 +33,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"storage": {
|
"storage": {
|
||||||
"type": "memcached",
|
"type": "file"
|
||||||
"host": "127.0.0.1",
|
|
||||||
"port": 11211,
|
|
||||||
"expire": 2592000
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"documents": {
|
"documents": {
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
version: '3.0'
|
||||||
|
services:
|
||||||
|
haste-server:
|
||||||
|
build: .
|
||||||
|
networks:
|
||||||
|
- db-network
|
||||||
|
environment:
|
||||||
|
- STORAGE_TYPE=memcached
|
||||||
|
- STORAGE_HOST=memcached
|
||||||
|
- STORAGE_PORT=11211
|
||||||
|
ports:
|
||||||
|
- 7777:7777
|
||||||
|
memcached:
|
||||||
|
image: memcached:latest
|
||||||
|
networks:
|
||||||
|
- db-network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
db-network:
|
|
@ -0,0 +1,108 @@
|
||||||
|
const {
|
||||||
|
HOST,
|
||||||
|
PORT,
|
||||||
|
KEY_LENGTH,
|
||||||
|
MAX_LENGTH,
|
||||||
|
STATIC_MAX_AGE,
|
||||||
|
RECOMPRESS_STATIC_ASSETS,
|
||||||
|
STORAGE_TYPE,
|
||||||
|
STORAGE_HOST,
|
||||||
|
STORAGE_PORT,
|
||||||
|
STORAGE_EXPIRE_SECONDS,
|
||||||
|
STORAGE_DB,
|
||||||
|
STORAGE_AWS_BUCKET,
|
||||||
|
STORAGE_AWS_REGION,
|
||||||
|
STORAGE_PASSWORD,
|
||||||
|
STORAGE_USERNAME,
|
||||||
|
STORAGE_FILEPATH,
|
||||||
|
LOGGING_LEVEL,
|
||||||
|
LOGGING_TYPE,
|
||||||
|
LOGGING_COLORIZE,
|
||||||
|
KEYGENERATOR_TYPE,
|
||||||
|
KEY_GENERATOR_KEYSPACE,
|
||||||
|
RATE_LIMITS_NORMAL_TOTAL_REQUESTS,
|
||||||
|
RATE_LIMITS_NORMAL_EVERY_MILLISECONDS,
|
||||||
|
RATE_LIMITS_WHITELIST_TOTAL_REQUESTS,
|
||||||
|
RATE_LIMITS_WHITELIST_EVERY_MILLISECONDS,
|
||||||
|
RATE_LIMITS_WHITELIST,
|
||||||
|
RATE_LIMITS_BLACKLIST_TOTAL_REQUESTS,
|
||||||
|
RATE_LIMITS_BLACKLIST_EVERY_MILLISECONDS,
|
||||||
|
RATE_LIMITS_BLACKLIST,
|
||||||
|
DOCUMENTS,
|
||||||
|
} = process.env;
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
host: HOST,
|
||||||
|
port: PORT,
|
||||||
|
|
||||||
|
keyLength: KEY_LENGTH,
|
||||||
|
|
||||||
|
maxLength: MAX_LENGTH,
|
||||||
|
|
||||||
|
staticMaxAge: STATIC_MAX_AGE,
|
||||||
|
|
||||||
|
recompressStaticAssets: RECOMPRESS_STATIC_ASSETS,
|
||||||
|
|
||||||
|
logging: [
|
||||||
|
{
|
||||||
|
level: LOGGING_LEVEL,
|
||||||
|
type: LOGGING_TYPE,
|
||||||
|
colorize: LOGGING_COLORIZE,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
keyGenerator: {
|
||||||
|
type: KEYGENERATOR_TYPE,
|
||||||
|
keyspace: KEY_GENERATOR_KEYSPACE,
|
||||||
|
},
|
||||||
|
|
||||||
|
rateLimits: {
|
||||||
|
whitelist: RATE_LIMITS_WHITELIST ? RATE_LIMITS_WHITELIST.split(",") : [],
|
||||||
|
blacklist: RATE_LIMITS_BLACKLIST ? RATE_LIMITS_BLACKLIST.split(",") : [],
|
||||||
|
categories: {
|
||||||
|
normal: {
|
||||||
|
totalRequests: RATE_LIMITS_NORMAL_TOTAL_REQUESTS,
|
||||||
|
every: RATE_LIMITS_NORMAL_EVERY_MILLISECONDS,
|
||||||
|
},
|
||||||
|
whitelist:
|
||||||
|
RATE_LIMITS_WHITELIST_EVERY_MILLISECONDS ||
|
||||||
|
RATE_LIMITS_WHITELIST_TOTAL_REQUESTS
|
||||||
|
? {
|
||||||
|
totalRequests: RATE_LIMITS_WHITELIST_TOTAL_REQUESTS,
|
||||||
|
every: RATE_LIMITS_WHITELIST_EVERY_MILLISECONDS,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
blacklist:
|
||||||
|
RATE_LIMITS_BLACKLIST_EVERY_MILLISECONDS ||
|
||||||
|
RATE_LIMITS_BLACKLIST_TOTAL_REQUESTS
|
||||||
|
? {
|
||||||
|
totalRequests: RATE_LIMITS_WHITELIST_TOTAL_REQUESTS,
|
||||||
|
every: RATE_LIMITS_BLACKLIST_EVERY_MILLISECONDS,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
storage: {
|
||||||
|
type: STORAGE_TYPE,
|
||||||
|
host: STORAGE_HOST,
|
||||||
|
port: STORAGE_PORT,
|
||||||
|
expire: STORAGE_EXPIRE_SECONDS,
|
||||||
|
bucket: STORAGE_AWS_BUCKET,
|
||||||
|
region: STORAGE_AWS_REGION,
|
||||||
|
connectionUrl: `postgres://${STORAGE_USERNAME}:${STORAGE_PASSWORD}@${STORAGE_HOST}:${STORAGE_PORT}/${STORAGE_DB}`,
|
||||||
|
db: STORAGE_DB,
|
||||||
|
user: STORAGE_USERNAME,
|
||||||
|
password: STORAGE_PASSWORD,
|
||||||
|
path: STORAGE_FILEPATH,
|
||||||
|
},
|
||||||
|
|
||||||
|
documents: DOCUMENTS
|
||||||
|
? DOCUMENTS.split(",").reduce((acc, item) => {
|
||||||
|
const keyAndValueArray = item.replace(/\s/g, "").split("=");
|
||||||
|
return { ...acc, [keyAndValueArray[0]]: keyAndValueArray[1] };
|
||||||
|
}, {})
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(JSON.stringify(config));
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# We use this file to translate environmental variables to .env files used by the application
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
node ./docker-entrypoint.js > ./config.js
|
||||||
|
|
||||||
|
exec "$@"
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*global require,module,process*/
|
||||||
|
|
||||||
|
var AWS = require('aws-sdk');
|
||||||
|
var winston = require('winston');
|
||||||
|
|
||||||
|
var AmazonS3DocumentStore = function(options) {
|
||||||
|
this.expire = options.expire;
|
||||||
|
this.bucket = options.bucket;
|
||||||
|
this.client = new AWS.S3({region: options.region});
|
||||||
|
};
|
||||||
|
|
||||||
|
AmazonS3DocumentStore.prototype.get = function(key, callback, skipExpire) {
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
var req = {
|
||||||
|
Bucket: _this.bucket,
|
||||||
|
Key: key
|
||||||
|
};
|
||||||
|
|
||||||
|
_this.client.getObject(req, function(err, data) {
|
||||||
|
if(err) {
|
||||||
|
callback(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callback(data.Body.toString('utf-8'));
|
||||||
|
if (_this.expire && !skipExpire) {
|
||||||
|
winston.warn('amazon s3 store cannot set expirations on keys');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
AmazonS3DocumentStore.prototype.set = function(key, data, callback, skipExpire) {
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
var req = {
|
||||||
|
Bucket: _this.bucket,
|
||||||
|
Key: key,
|
||||||
|
Body: data,
|
||||||
|
ContentType: 'text/plain'
|
||||||
|
};
|
||||||
|
|
||||||
|
_this.client.putObject(req, function(err, data) {
|
||||||
|
if (err) {
|
||||||
|
callback(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callback(true);
|
||||||
|
if (_this.expire && !skipExpire) {
|
||||||
|
winston.warn('amazon s3 store cannot set expirations on keys');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AmazonS3DocumentStore;
|
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
|
@ -14,25 +14,21 @@
|
||||||
},
|
},
|
||||||
"main": "haste",
|
"main": "haste",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"busboy": "0.2.4",
|
||||||
|
"connect": "^3.7.0",
|
||||||
"connect-ratelimit": "0.0.7",
|
"connect-ratelimit": "0.0.7",
|
||||||
"connect-route": "0.1.5",
|
"connect-route": "0.1.5",
|
||||||
"connect": "3.4.1",
|
"pg": "^6.0.0",
|
||||||
"st": "1.1.0",
|
|
||||||
"winston": "0.6.2",
|
|
||||||
"redis-url": "0.1.0",
|
|
||||||
"redis": "0.8.1",
|
"redis": "0.8.1",
|
||||||
|
"redis-url": "0.1.0",
|
||||||
|
"st": "^2.0.0",
|
||||||
"uglify-js": "3.1.6",
|
"uglify-js": "3.1.6",
|
||||||
"busboy": "0.2.4",
|
"winston": "^2.0.0"
|
||||||
"pg": "4.1.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mocha": "^4.0.1"
|
"mocha": "^8.1.3"
|
||||||
},
|
},
|
||||||
"bundledDependencies": [],
|
"bundledDependencies": [],
|
||||||
"engines": {
|
|
||||||
"node": "8.1.4",
|
|
||||||
"npm": "5.2.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"haste-server": "./server.js"
|
"haste-server": "./server.js"
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,6 +17,8 @@ textarea {
|
||||||
outline: none;
|
outline: none;
|
||||||
resize: none;
|
resize: none;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* the line numbers */
|
/* the line numbers */
|
||||||
|
@ -42,7 +44,6 @@ textarea {
|
||||||
border: 0px;
|
border: 0px;
|
||||||
outline: none;
|
outline: none;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding-right: 360px;
|
|
||||||
overflow: inherit;
|
overflow: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -170,8 +170,7 @@ haste.extensionMap = {
|
||||||
lua: 'lua', pas: 'delphi', java: 'java', cpp: 'cpp', cc: 'cpp', m: 'objectivec',
|
lua: 'lua', pas: 'delphi', java: 'java', cpp: 'cpp', cc: 'cpp', m: 'objectivec',
|
||||||
vala: 'vala', sql: 'sql', sm: 'smalltalk', lisp: 'lisp', ini: 'ini',
|
vala: 'vala', sql: 'sql', sm: 'smalltalk', lisp: 'lisp', ini: 'ini',
|
||||||
diff: 'diff', bash: 'bash', sh: 'bash', tex: 'tex', erl: 'erlang', hs: 'haskell',
|
diff: 'diff', bash: 'bash', sh: 'bash', tex: 'tex', erl: 'erlang', hs: 'haskell',
|
||||||
md: 'markdown', txt: '', coffee: 'coffee', json: 'javascript',
|
md: 'markdown', txt: '', coffee: 'coffee', swift: 'swift'
|
||||||
swift: 'swift'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Look up the extension preferred for a type
|
// Look up the extension preferred for a type
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue