diff --git a/.gitignore b/.gitignore index 639fd3e..b5bb9e2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ private.* log/* .DS_Store result.json -npm-debug.log \ No newline at end of file +npm-debug.log +.vscode +config.json \ No newline at end of file diff --git a/README.md b/README.md index 1d51768..e3ad011 100644 --- a/README.md +++ b/README.md @@ -1,115 +1,35 @@ -# 此项目已不再维护,有需要者,欢迎 fork -苦于工作繁忙,我已经不刷题很久了,没有使用需求就难以保持爬虫逻辑紧跟 leetcode 官网更新。欢迎 fork,解决需求。 - -# leetcode-spider [](https://www.npmjs.com/package/leetcode-spider) - -使用 JS 编写的 leetcode 解题源码爬虫.爬取你自己的 leetcode 解题源码. - -如果你也想把你在 [leetcode](https://leetcode.com/) 上提交且 accepted 的解题代码爬下来,那么本工具就是为此需求而生!爬下来的代码可以放在 github 上管理和开源出来,可以作为个人展示,更可以借助 [leetcode-viewer](https://github.com/Ma63d/leetcode-viewer) 将代码通过一个单页应用完美展现,几条命令就可以呈现一个 leetcode 源码博客,交流和展示搞起来! - -## 新版本来了 - -- 支持使用模板语法来配置你自己的 README.md 模板了! -- 新增部分配置项 -- 优化 README.md 输出: -  -- 优化网络错误处理机制 - - -**需要 Node 4.0 及以上版本!** - -## 安装 Installation - -``` -npm i leetcode-spider -g -``` - - -## 使用 Usage - -请事先建立好如下 json 文件(以命名为 config.json 为例): - - -``` -{ - "username" : "hello@gmail.com", - "password" : "xxxxxxxxx", - "language": ["java","c++","c"], - "outputDir": "./solutions", (可选字段) - "template": "./README.tpl" (可选字段) -} -``` - -- `username` 和 `password` 对应你的的 leetcode 账户. - - -- `language` 对应于你用来解 leetcode 的编程语言,该项为一个数组,即使只有一种语言. -目前 `language` 字段支持填写所有 leetcode 的编程语言: - - `c++`(别填`cpp`) - - `c` - - `java` - - `javascript` (别填`js`) - - `python` - - `c#` (别填`csharp`) - - `ruby` - - `swift` - - `go` - -- `outputDir` **选填**,表示你希望存放源码文件的目录,默认`"./solutions"` -- `template` **选填**,表示你自己定义的 README.tpl 路径,默认 "./README.tpl" - - -## 运行 Execution - -``` -lc-spider // 默认使用config.json为配置文件运行爬虫 -``` -**程序会记录上一次爬取了哪些题目,之前爬取过的题目再次运行的时候不会爬取,除非你通过-n选项手动指定.** - -**这也意味着,当你在进行增量爬取时,根本不需要去指定要爬哪些题目, leetcode-spider 会自动知道哪些题目需要爬.** - -举个例子,按照我们的日常使用: - -* 当你昨天 A 了5道题,你用爬虫爬了下来 -* 然后今天你 A 了6道题,你今天再次运行程序 - -程序此时会自动检查你跟上一次爬取结果相比多写了哪些题,然后把这些新增的代码爬取下来,不会重复的去爬取,你也不用手工指定你今天 AC 了哪些题. - -永远只用一行命令: `lc-spider`.(除非你想要再去爬以前爬过的题,比如你改进了修改了原先AC的代码,想把新代码爬下来,或者你新增了另一种语言的解法, 那么这种时候可以用-n选项指定具体要爬取的题目,请参考[选项](https://github.com/Ma63d/leetcode-spider#选项)章节的具体内容.) - -此外源码对应的 leetcode 的题目,也会爬取下来,放在代码目录, markdown 格式. - -爬取完成后会自动生成 README.md 文件,当你把爬下来的代码放在 github 上时,README.md 起一个介绍和导航的作用.另外,有的同学的 config.json 文件是直接放在当前的代码存放目录的,那你在把这个目录上传到 github 上之前,请记得写 `.gitignore` 文件,在里面忽略掉你的 config.json 文件!不然你的用户名和密码也传到 github 上公开给大家了(虽然 leetcode 账户屁用没有)。 - -如果你运行 lc-spider 却显示无法找到命令,首先请确认一下你在 npm 安装 lc-spider 的时候是否是全局安装(也就是有没有那个`-g`),如果你是全局安装的,那就是你的 npm 的环境变量配置得不对了,请参考百度的 fis 团队写的 [这篇文章](https://github.com/fex-team/fis/issues/565),方便不熟悉 npm 的同学解决自己遇到的问题. - -## 模板功能 - -嫌默认生成自带的 README.md 不好看?嫌我写的文案不够好? -没关系,现在你可以自定义你的 README.md 模板,使用 [mustache](https://github.com/janl/mustache.js) 语法(就是 vue 用的那一套),自己书写你的 README.md 模板: - -leetcode-spider 第一次运行之后会在你运行命令的目录下生成 [README.tpl](https://github.com/Ma63d/leetcode-spider/blob/master/lib/README.tpl) 文件,如果你对默认生成的 README.md 文件不满意,那你自行修改这个 README.tpl 模板文件即可。程序会在下次运行时使用这个模板文件来生成 README.md . - - - -## 帮助 Help -``` -lc-spider -h -``` - -## 选项 - -### -config or -c -``` -lc-spider -c xxx.json -``` - -使用指定的配置文件运行 lc-spider. 默认使用的是 config.json. - -### -number or -n -``` -lc-spider -n 2-15 3 78-101 -``` - -只爬取你指定的题目,可以使用连字符(如15-100),此处指定的是需要爬取的题号.程序会检查哪些题目你 AC 了,因此你可以放心的填写.比如你a了200-300之间的某几道题,但是不想一个个的指定出来,那你就大胆的写上200-300,程序会查找200-300范围内你 AC 的源码,并爬取下来. +
+
+
+
+
+
+ Language: java c++
+
+ Last updated: 2020-03-29
+
+
The source code is fetched using the tool leetcode-spider.
+ +| # | Problems | Solutions | Difficulty | Acceptance | Paid-Only +|:--:|:-----:|:---------:|:----:|:----:|:----:| +|169|[majority-element](https://leetcode-cn.com/problems/majority-element/)| [java](./solutions/169.majority-element/majority-element.java)|Easy|62.80%|| +|300|[longest-increasing-subsequence](https://leetcode-cn.com/problems/longest-increasing-subsequence/)| [java](./solutions/300.longest-increasing-subsequence/longest-increasing-subsequence.java)|Medium|44.00%|| +|365|[water-and-jug-problem](https://leetcode-cn.com/problems/water-and-jug-problem/)| [java](./solutions/365.water-and-jug-problem/water-and-jug-problem.java)|Medium|35.11%|| +|409|[longest-palindrome](https://leetcode-cn.com/problems/longest-palindrome/)| [java](./solutions/409.longest-palindrome/longest-palindrome.java)|Easy|54.93%|| +|695|[max-area-of-island](https://leetcode-cn.com/problems/max-area-of-island/)| [java](./solutions/695.max-area-of-island/max-area-of-island.java)|Medium|63.12%|| +|820|[short-encoding-of-words](https://leetcode-cn.com/problems/short-encoding-of-words/)| [java](./solutions/820.short-encoding-of-words/short-encoding-of-words.java)|Medium|48.32%|| +|836|[rectangle-overlap](https://leetcode-cn.com/problems/rectangle-overlap/)| [java](./solutions/836.rectangle-overlap/rectangle-overlap.java)|Easy|49.87%|| +|876|[middle-of-the-linked-list](https://leetcode-cn.com/problems/middle-of-the-linked-list/)| [java](./solutions/876.middle-of-the-linked-list/middle-of-the-linked-list.java)|Easy|68.67%|| +|892|[surface-area-of-3d-shapes](https://leetcode-cn.com/problems/surface-area-of-3d-shapes/)| [java](./solutions/892.surface-area-of-3d-shapes/surface-area-of-3d-shapes.java)|Easy|64.39%|| +|914|[x-of-a-kind-in-a-deck-of-cards](https://leetcode-cn.com/problems/x-of-a-kind-in-a-deck-of-cards/)| [java](./solutions/914.x-of-a-kind-in-a-deck-of-cards/x-of-a-kind-in-a-deck-of-cards.java)|Easy|39.32%|| +|945|[minimum-increment-to-make-array-unique](https://leetcode-cn.com/problems/minimum-increment-to-make-array-unique/)| [java](./solutions/945.minimum-increment-to-make-array-unique/minimum-increment-to-make-array-unique.java)|Medium|47.56%|| +|999|[available-captures-for-rook](https://leetcode-cn.com/problems/available-captures-for-rook/)| [java](./solutions/999.available-captures-for-rook/available-captures-for-rook.java)|Easy|69.37%|| +|1160|[find-words-that-can-be-formed-by-characters](https://leetcode-cn.com/problems/find-words-that-can-be-formed-by-characters/)| [java](./solutions/1160.find-words-that-can-be-formed-by-characters/find-words-that-can-be-formed-by-characters.java)|Easy|69.48%|| +|1162|[as-far-from-land-as-possible](https://leetcode-cn.com/problems/as-far-from-land-as-possible/)| [java](./solutions/1162.as-far-from-land-as-possible/as-far-from-land-as-possible.java)|Medium|43.93%|| diff --git a/README.tpl b/README.tpl new file mode 100644 index 0000000..4b115c5 --- /dev/null +++ b/README.tpl @@ -0,0 +1,24 @@ +
+
+
+
+
+
+ Language: {{language}}
+
+ Last updated: {{time}}
+
+
The source code is fetched using the tool leetcode-spider.
+ +| # | Problems | Solutions | Difficulty | Acceptance | Paid-Only +|:--:|:-----:|:---------:|:----:|:----:|:----:| +{{#solutions}} +|{{id}}|[{{title}}](https://leetcode-cn.com/problems/{{title}}/)|{{solutionLinks}}|{{difficulty}}|{{acceptance}}|{{paidOnly}}| +{{/solutions}} + diff --git a/lib/README.tpl b/lib/README.tpl index fa7970a..4b115c5 100644 --- a/lib/README.tpl +++ b/lib/README.tpl @@ -19,6 +19,6 @@ | # | Problems | Solutions | Difficulty | Acceptance | Paid-Only |:--:|:-----:|:---------:|:----:|:----:|:----:| {{#solutions}} -|{{id}}|[{{title}}](https://leetcode.com/problems/{{title}}/)|{{solutionLinks}}|{{difficulty}}|{{acceptance}}|{{paidOnly}}| +|{{id}}|[{{title}}](https://leetcode-cn.com/problems/{{title}}/)|{{solutionLinks}}|{{difficulty}}|{{acceptance}}|{{paidOnly}}| {{/solutions}} diff --git a/lib/file.js b/lib/file.js index 519522a..43c2dd2 100644 --- a/lib/file.js +++ b/lib/file.js @@ -31,7 +31,7 @@ let write = co.wrap(function * (pathToWrite, content) { * @api public */ exports.writeToFile = co.wrap(function * (languageCodeMap, solutionsDirPath) { - const outputPath = path.resolve(solutionsDirPath, formatId(languageCodeMap._id) + '.' + languageCodeMap._title) + const outputPath = path.resolve(solutionsDirPath, languageCodeMap._frontendId + '.' + languageCodeMap._title) ensureDirectoryExists(outputPath) try { let solveLang = [] @@ -51,19 +51,19 @@ exports.writeToFile = co.wrap(function * (languageCodeMap, solutionsDirPath) { } }) -/** - * Format id to 3-bit. - * - * @param {Number} id - * @return {String} - * @api public - */ -function formatId (id) { - if (id < 10) { - return '00' + id - } else if (id < 100) { - return '0' + id - } else { - return '' + id - } -} +// /** +// * Format id to 3-bit. +// * +// * @param {Number} id +// * @return {String} +// * @api public +// */ +// function formatId (id) { +// if (id < 10) { +// return '00' + id +// } else if (id < 100) { +// return '0' + id +// } else { +// return '' + id +// } +// } diff --git a/lib/generateMD.js b/lib/generateMD.js index 8677174..fbd1824 100644 --- a/lib/generateMD.js +++ b/lib/generateMD.js @@ -39,7 +39,7 @@ exports.generateMarkdown = co.wrap(function * (resultObj, leetcodeNumObj, output }).sort() let solutions = [] problemNumbers.forEach(id => { - let idStr = formatId(id) + let idStr = id let solutionLinks = `` resultObj[id].language.forEach(lang => { solutionLinks += ` [${lang}](./${outputDir}/${idStr}.${resultObj[id].title}/${resultObj[id].title}.${language.nameMap[lang]})` @@ -102,15 +102,15 @@ function getTimeStr (fmt) { return fmt } -function formatId (id) { - if (id < 10) { - return '00' + id - } else if (id < 100) { - return '0' + id - } else { - return '' + id - } -} +// function formatId (id) { +// if (id < 10) { +// return '00' + id +// } else if (id < 100) { +// return '0' + id +// } else { +// return '' + id +// } +// } function leveToStr (level) { switch (level) { case 1: return 'Easy' diff --git a/lib/result.js b/lib/result.js index 6252a6c..af30d0a 100644 --- a/lib/result.js +++ b/lib/result.js @@ -3,6 +3,7 @@ const fs = require('fs') const co = require('co') const thenifyAll = require('thenify-all') const thenFs = thenifyAll(fs, {}, ['stat', 'unlink', 'writeFile']) +const logger = require('log4js').getLogger('layout-pattern') let result @@ -14,7 +15,7 @@ let result * @return {Object} last spider result * @api public */ -exports.getResult = co.wrap(function * (resultJsonPath) { +exports.getResult = co.wrap(function* (resultJsonPath) { try { let stats = yield thenFs.stat(resultJsonPath) if (stats.isFile()) { @@ -39,10 +40,11 @@ exports.getResult = co.wrap(function * (resultJsonPath) { * @return {Object} result * @api public */ -exports.writeResult = co.wrap(function * (languageCodeMapArr, leetcodeNumObj, resultJsonPath) { +exports.writeResult = co.wrap(function* (languageCodeMapArr, leetcodeNumObj, resultJsonPath) { + logger.info("Build result.json") languageCodeMapArr.forEach(element => { let ele = {} - result[element['_id']] = ele + result[element['_frontendId']] = ele ele.id = element['_id'] ele.level = element['_level'] ele.title = element['_title'] @@ -50,9 +52,10 @@ exports.writeResult = co.wrap(function * (languageCodeMapArr, leetcodeNumObj, re ele.acceptance = element['_acceptance'] ele.language = element['_solveLang'] }) - yield thenFs.unlink(resultJsonPath).catch(function () {}) + yield thenFs.unlink(resultJsonPath).catch(function () { }) result.lastUpdatedTime = getTimeStr('yyyy-MM-dd') Object.assign(result, leetcodeNumObj) + logger.info("Write to result.json : " + JSON.stringify(result)) yield thenFs.writeFile(resultJsonPath, JSON.stringify(result)) return result }) @@ -64,7 +67,7 @@ exports.writeResult = co.wrap(function * (languageCodeMapArr, leetcodeNumObj, re * @return {String} * @api public */ -function getTimeStr (fmt) { +function getTimeStr(fmt) { let time = new Date() let o = { 'M+': time.getMonth() + 1, // 月份 diff --git a/lib/runtimeConf.js b/lib/runtimeConf.js index 08085fe..9cd9632 100644 --- a/lib/runtimeConf.js +++ b/lib/runtimeConf.js @@ -1,4 +1,4 @@ 'use strict' module.exports = { - concurrency: 30 + concurrency: 4 } diff --git a/lib/spider.js b/lib/spider.js index 635f5d0..4e3b9aa 100644 --- a/lib/spider.js +++ b/lib/spider.js @@ -4,26 +4,27 @@ const assert = require('assert') const path = require('path') const logger = require('log4js').getLogger('layout-pattern') -const baseUrl = 'https://leetcode.com/' const requestLib = require('request') const jar = requestLib.jar() + +let thenifyAll = require('thenify-all') + // set default http request settings let request = requestLib .defaults({ jar: jar, - baseUrl: baseUrl, + baseUrl: 'https://leetcode-cn.com/', headers: { - 'Host': 'leetcode.com', + 'Host': 'leetcode-cn.com', 'Cache-Control': 'max-age=0', 'Upgrade-Insecure-Requests': '1', - 'Referer': 'https://leetcode.com/accounts/login/', + 'Referer': 'https://leetcode-cn.com/accounts/login/', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6', 'Content-Type': 'application/x-www-form-urlencoded' } }) -let thenifyAll = require('thenify-all') // promisify the callback-based request API request = thenifyAll(request, {}, ['get', 'post']) let cheerio = require('cheerio') @@ -43,7 +44,7 @@ let solutionsDirPath let resultJsonPath let leetcodeNumObj = {} -exports.fetch = co.wrap(function * (conf, numObj) { +exports.fetch = co.wrap(function* (conf, numObj) { config = conf solutionsDirPath = path.resolve(process.cwd(), config.outputDir) resultJsonPath = path.resolve(solutionsDirPath, 'result.json') @@ -66,7 +67,7 @@ exports.fetch = co.wrap(function * (conf, numObj) { let fetchList try { if (undefined === numObj) { - fetchList = yield fetchSolutionsNotEverFetched(acList) + fetchList = yield fetchSolutionsNotEverFetched(acList, conf.language[0]) } else { fetchList = yield fetchWithGivenNumber(acList, numObj) } @@ -93,6 +94,21 @@ exports.fetch = co.wrap(function * (conf, numObj) { yield generateMDUtils.generateMarkdown(resultObj, leetcodeNumObj, config.outputDir, config.template) }) +let graphql = co.wrap(function* (opName, query, variables) { + let obj = { operationName: opName, query: query, variables: variables } + let content = JSON.stringify(obj) + let resp = yield request({ + method: 'POST', + url: '/graphql', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': content.length + }, + body: content + }) + return JSON.parse(resp[1]) +}) + /** * Use the config json file to login * and return a promise. @@ -101,20 +117,21 @@ exports.fetch = co.wrap(function * (conf, numObj) { * @return {Promise} * @api public */ -let login = co.wrap(function * (conf) { +let login = co.wrap(function* (conf) { config = conf - let responseAndBody = yield request.get('/accounts/login/') + let responseAndBody = yield request.get('/') let $ = cheerio.load(responseAndBody[1]) let token = $('input[name=csrfmiddlewaretoken]').val() logger.info('token get') debug('token:' + token) - let cookie = jar.getCookies('https://leetcode.com/') - let cookieOfToken = cookie.find(element => element.key === 'csrftoken') - assert.notEqual(cookieOfToken, undefined, 'network error: cannot get csrftoken') + let cookie = jar.getCookies('https://leetcode-cn.com/') + // let cookieOfToken = cookie.find(element => element.key === 'csrftoken') + // assert.notEqual(cookieOfToken, undefined, 'network error: cannot get csrftoken') - yield request({method: 'POST', + yield request({ + method: 'POST', url: '/accounts/login/', form: { 'csrfmiddlewaretoken': token, @@ -122,7 +139,7 @@ let login = co.wrap(function * (conf) { 'password': conf['password'] } }) - cookie = jar.getCookies('https://leetcode.com/') + cookie = jar.getCookies('https://leetcode-cn.com/') assert.notEqual(cookie.find(element => element.key === 'LEETCODE_SESSION'), undefined, 'incorrect username or password') logger.info('login successfully') }) @@ -134,10 +151,10 @@ let login = co.wrap(function * (conf) { * @return {Array} list of solutions needed to fetch * @api public */ -let fetchACLists = co.wrap(function * () { +let fetchACLists = co.wrap(function* () { debug('fetch the accepted solutions\' list') - let [, body] = yield request.get('/api/problems/algorithms/') + let [, body] = yield request.get('/api/problems/all/') try { body = JSON.parse(body) } catch (e) { @@ -150,7 +167,7 @@ let fetchACLists = co.wrap(function * () { return body['stat_status_pairs'] }) -let fetchWithGivenNumber = co.wrap(function * (acLists, numObj) { +let fetchWithGivenNumber = co.wrap(function* (acLists, numObj) { yield resultUtils.getResult() return acLists.filter(element => { if (element['paid_only']) { @@ -162,7 +179,7 @@ let fetchWithGivenNumber = co.wrap(function * (acLists, numObj) { }).reverse() }) -let fetchSolutionsNotEverFetched = co.wrap(function * (acLists) { +let fetchSolutionsNotEverFetched = co.wrap(function* (acLists, lang) { // load the last spider result from result.json // if result.json doesn't exist that means never fetched solutions before let result = yield resultUtils.getResult(resultJsonPath) @@ -173,7 +190,15 @@ let fetchSolutionsNotEverFetched = co.wrap(function * (acLists) { } //! (element.stat['question_id'] in result) // if we fetched once we will not fetch this problem any more - return element.status === 'ac' && !(element.stat['question_id'] in result) + // if due to network error, source file hasn't been fetched(xxx.java or xxx.cpp not existed), then fetch again. + if (element.status === 'ac') { + if (!(element.stat['frontend_question_id'] in result)) { + return true + } + return result[element.stat['frontend_question_id']]["language"].indexOf(lang) === -1 + } else { + return false + } }).reverse() }) @@ -184,7 +209,7 @@ let fetchSolutionsNotEverFetched = co.wrap(function * (acLists) { * @return {Promise} * @api public */ -let parallelFetch = co.wrap(function * (acLists) { +let parallelFetch = co.wrap(function* (acLists) { debug('fetch solutions ') if (acLists && acLists.length > 0) { // form the promises array @@ -197,6 +222,7 @@ let parallelFetch = co.wrap(function * (acLists) { // store the problem's info in languageCodeMap languageCodeMap._title = acProblem.stat['question__title_slug'] languageCodeMap._id = acProblem.stat['question_id'] + languageCodeMap._frontendId = acProblem.stat['frontend_question_id'] languageCodeMap._level = acProblem['difficulty']['level'] languageCodeMap._paid_only = acProblem['paid_only'] languageCodeMap._acceptance = (acProblem['stat']['total_acs'] / acProblem['stat']['total_submitted'] * 100).toFixed(2) + '%' @@ -230,7 +256,7 @@ let parallelFetch = co.wrap(function * (acLists) { * @return {Object} languageCodeMap * @api public */ -let fetchAndWrite = function * (problemInfo, languageToFetch, languageCodeMap) { +let fetchAndWrite = function* (problemInfo, languageToFetch, languageCodeMap) { yield fetchACSolutionOfProblem(problemInfo, languageToFetch, 0, languageCodeMap) yield fetchQuestion(problemInfo, languageCodeMap).catch(err => { throw err }) yield fileUtils.writeToFile(languageCodeMap, solutionsDirPath).catch(err => { throw err }) @@ -249,7 +275,7 @@ let fetchAndWrite = function * (problemInfo, languageToFetch, languageCodeMap) { * @return {Object} languageCodeMap * @api public */ -let fetchACSolutionOfProblem = co.wrap(function * (problemInfo, languageToFetch, page, languageCodeMap) { +let fetchACSolutionOfProblem = co.wrap(function* (problemInfo, languageToFetch, page, languageCodeMap) { debug('fetch ' + problemInfo.stat['question__title_slug']) if (languageToFetch.length < 1) { @@ -258,25 +284,48 @@ let fetchACSolutionOfProblem = co.wrap(function * (problemInfo, languageToFetch, let responseAndBody, submissionsJson try { - responseAndBody = yield request.get({ - url: '/api/submissions/' + problemInfo.stat['question__title_slug'] + '/?offset=' + (page * 50) + '&limit=50', - headers: { - 'Accept': '*/*' - } - }) - submissionsJson = JSON.parse(responseAndBody[1])['submissions_dump'] + responseAndBody = yield graphql( + 'Submissions', + `query Submissions($offset: Int!, $limit: Int!, $lastKey: String, $questionSlug: String!) { + submissionList(offset: $offset, limit: $limit, lastKey: $lastKey, questionSlug: $questionSlug) { + lastKey + hasNext + submissions { + id + statusDisplay + lang + runtime + timestamp + url + isPending + memory + __typename + } + __typename + } + }`, + { offset: 0, limit: 20, lastKey: null, questionSlug: problemInfo.stat['question__title_slug'] },request) + // responseAndBody = yield request.get({ + // url: '/api/submissions/' + problemInfo.stat['question__title_slug'] + '/?offset=' + (page * 50) + '&limit=50' + // }) + // submissionsJson = JSON.parse(responseAndBody[1])['submissions_dump'] + submissionsJson = responseAndBody.data.submissionList.submissions } catch (err) { logger.error('Fetching submissions of ' + problemInfo.stat['question__title_slug'] + ' failed') logger.error(err.stack) return } + if (submissionsJson == undefined) { + return + } + // form the promises array let acSolutionPromise = [] // check the submissions list submissionsJson.forEach(e => { - if (e['status_display'] !== 'Accepted') { + if (e.statusDisplay !== 'Accepted') { return } let language = languageLeetcodeNameMap[e['lang']] @@ -293,23 +342,11 @@ let fetchACSolutionOfProblem = co.wrap(function * (problemInfo, languageToFetch, } }) - // if no solution can be fetched - if (acSolutionPromise.length < 1) { - if (submissionsJson.length === 50 && languageToFetch.length > 0) { - // then fetch the next page of submissions - return yield fetchACSolutionOfProblem(problemInfo, languageToFetch, page + 1, languageCodeMap) - } else { - return languageCodeMap - } - } else { + if (acSolutionPromise.length > 0) { yield acSolutionPromise - if (submissionsJson.length === 50 && languageToFetch.length > 0) { - return yield fetchACSolutionOfProblem(problemInfo, languageToFetch, page + 1, languageCodeMap) - } else { - // fetching finished , let's return it - return languageCodeMap - } } + + return languageCodeMap }) /** @@ -321,19 +358,81 @@ let fetchACSolutionOfProblem = co.wrap(function * (problemInfo, languageToFetch, * @return {Promise} * @api public */ -let fetchQuestion = co.wrap(function * (problemInfo, languageCodeMap) { +let fetchQuestion = co.wrap(function* (problemInfo, languageCodeMap) { let responseAndBody try { - responseAndBody = yield request.get('/problems/' + problemInfo.stat['question__title_slug'] + '/') + responseAndBody = yield graphql( + "questionData", + `query questionData($titleSlug: String!) { + question(titleSlug: $titleSlug) { + questionId + questionFrontendId + boundTopicId + title + titleSlug + content + translatedTitle + translatedContent + isPaidOnly + difficulty + likes + dislikes + isLiked + similarQuestions + contributors { + username + profileUrl + avatarUrl + __typename + } + langToValidPlayground + topicTags { + name + slug + translatedName + __typename + } + companyTagStats + codeSnippets { + lang + langSlug + code + __typename + } + stats + hints + solution { + id + canSeeDetail + __typename + } + status + sampleTestCase + metaData + judgerAvailable + judgeType + mysqlSchemas + enableRunCode + enableTestMode + envInfo + __typename + } + } + `, + { titleSlug: problemInfo.stat['question__title_slug'] } + ) + // responseAndBody = yield request.get('/problems/' + problemInfo.stat['question__title_slug'] + '/') + languageCodeMap['_question'] = responseAndBody.data.question.content } catch (err) { debug('network error:error happened when fetching problem \'' + problemInfo.stat['question__title_slug'] + '\'') throw err }; - let $ = cheerio.load(responseAndBody[1]) + // let $ = cheerio.load(responseAndBody[1]) // debug('problem :'); // debug($('meta[name=description]').attr('content')); - languageCodeMap['_question'] = $('meta[name=description]').attr('content') + // languageCodeMap['_question'] = $('meta[name=description]').attr('content') + }) /** @@ -345,7 +444,7 @@ let fetchQuestion = co.wrap(function * (problemInfo, languageCodeMap) { * @return {Promise} * @api public */ -let fetchSolutionsOfUrl = co.wrap(function * (url, times) { +let fetchSolutionsOfUrl = co.wrap(function* (url, times) { let responseAndBody try { responseAndBody = yield request.get(url) @@ -356,7 +455,7 @@ let fetchSolutionsOfUrl = co.wrap(function * (url, times) { // so i repeat at most 5 times return yield co(fetchSolutionsOfUrl, url, ++times) } - throw new Error('network error:cannot get the page of url' + url) + throw new Error('network error:cannot get the page of url ' + url) }; let body = responseAndBody[1] @@ -367,8 +466,9 @@ let fetchSolutionsOfUrl = co.wrap(function * (url, times) { // so i repeat at most 5 times return yield fetchSolutionsOfUrl(url, ++times) } - debug('can not get full page of' + url) - throw new Error('network error:the page of' + url + 'is not complete') + + debug('can not get full page of ' + url) + throw new Error('network error:the page of ' + url + ' is not complete') } let codeInUnicode = matchResult[1] @@ -376,5 +476,5 @@ let fetchSolutionsOfUrl = co.wrap(function * (url, times) { /* eslint-disable no-eval */ let code = eval("'" + codeInUnicode + "'") debug(url + 'code get!') - return {code} + return { code } }) diff --git a/lib/util.js b/lib/util.js index bd36b12..65995c9 100644 --- a/lib/util.js +++ b/lib/util.js @@ -11,11 +11,17 @@ const path = require('path') * * @api public */ -function ensureDirectoryExists (dirname) { +function ensureDirectoryExists(dirname) { if (fs.existsSync(dirname)) { return true } ensureDirectoryExists(path.dirname(dirname)) fs.mkdirSync(dirname) } + +function fileExists(filename) { + return fs.existsSync(filename) +} + exports.ensureDirectoryExists = ensureDirectoryExists +exports.fileExists = fileExists \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..0ccda47 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1139 @@ +{ + "name": "leetcode-spider", + "version": "1.0.2", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "http://registry.npm.taobao.org/ansi-regex/download/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "http://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "any-promise": { + "version": "1.3.0", + "resolved": "http://registry.npm.taobao.org/any-promise/download/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "http://registry.npm.taobao.org/asn1/download/asn1-0.2.4.tgz", + "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "http://registry.npm.taobao.org/assert/download/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "requires": { + "util": "0.10.3" + } + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "http://registry.npm.taobao.org/assert-plus/download/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "http://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "http://registry.npm.taobao.org/aws-sign2/download/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "http://registry.npm.taobao.org/aws4/download/aws4-1.8.0.tgz", + "integrity": "sha1-8OAD2cqef1nHpQiUXXsu+aBKVC8=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/bcrypt-pbkdf/download/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/boolbase/download/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, + "boom": { + "version": "2.10.1", + "resolved": "http://registry.npm.taobao.org/boom/download/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "requires": { + "hoek": "2.x.x" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "http://registry.npm.taobao.org/builtin-modules/download/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, + "camelcase": { + "version": "3.0.0", + "resolved": "http://registry.npm.taobao.org/camelcase/download/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "caseless": { + "version": "0.11.0", + "resolved": "http://registry.npm.taobao.org/caseless/download/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cheerio": { + "version": "0.22.0", + "resolved": "http://registry.npm.taobao.org/cheerio/download/cheerio-0.22.0.tgz", + "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", + "requires": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash.assignin": "^4.0.9", + "lodash.bind": "^4.1.4", + "lodash.defaults": "^4.0.1", + "lodash.filter": "^4.4.0", + "lodash.flatten": "^4.2.0", + "lodash.foreach": "^4.3.0", + "lodash.map": "^4.4.0", + "lodash.merge": "^4.4.0", + "lodash.pick": "^4.2.1", + "lodash.reduce": "^4.4.0", + "lodash.reject": "^4.4.0", + "lodash.some": "^4.4.0" + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "http://registry.npm.taobao.org/cliui/download/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "http://registry.npm.taobao.org/co/download/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "co-parallel": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/co-parallel/download/co-parallel-1.0.0.tgz", + "integrity": "sha1-WFl6BlgAWkK4xqI8yE2ioQ2Ubyo=", + "requires": { + "co-thread": "0.0.1" + } + }, + "co-thread": { + "version": "0.0.1", + "resolved": "http://registry.npm.taobao.org/co-thread/download/co-thread-0.0.1.tgz", + "integrity": "sha1-V3E/DvS4flWV1PI3Ef/ks7beXnQ=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "http://registry.npm.taobao.org/code-point-at/download/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "http://registry.npm.taobao.org/combined-stream/download/combined-stream-1.0.7.tgz", + "integrity": "sha1-LR0kMXr7ir6V1tLAsHtXgTU52Cg=", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.19.0", + "resolved": "http://registry.npm.taobao.org/commander/download/commander-2.19.0.tgz", + "integrity": "sha1-9hmKqE5bg8RgVLlN3tv+1e6f8So=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/core-util-is/download/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "http://registry.npm.taobao.org/cryptiles/download/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "requires": { + "boom": "2.x.x" + } + }, + "css-select": { + "version": "1.2.0", + "resolved": "http://registry.npm.taobao.org/css-select/download/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-what": { + "version": "2.1.2", + "resolved": "http://registry.npm.taobao.org/css-what/download/css-what-2.1.2.tgz", + "integrity": "sha1-wIdtnQSAkn19SSDc1yrzWVZJVU0=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "http://registry.npm.taobao.org/dashdash/download/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/assert-plus/download/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "date-format": { + "version": "0.0.0", + "resolved": "http://registry.npm.taobao.org/date-format/download/date-format-0.0.0.tgz", + "integrity": "sha1-CSBoY6sHDrRZrOpVQsvYVrEZZrM=" + }, + "debug": { + "version": "2.6.9", + "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "http://registry.npm.taobao.org/decamelize/download/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/delayed-stream/download/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "dom-serializer": { + "version": "0.1.0", + "resolved": "http://registry.npm.taobao.org/dom-serializer/download/dom-serializer-0.1.0.tgz", + "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", + "requires": { + "domelementtype": "~1.1.1", + "entities": "~1.1.1" + }, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "http://registry.npm.taobao.org/domelementtype/download/domelementtype-1.1.3.tgz", + "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=" + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "http://registry.npm.taobao.org/domelementtype/download/domelementtype-1.3.1.tgz", + "integrity": "sha1-0EjESzew0Qp/Kj1f7j9DM9eQSB8=" + }, + "domhandler": { + "version": "2.4.2", + "resolved": "http://registry.npm.taobao.org/domhandler/download/domhandler-2.4.2.tgz", + "integrity": "sha1-iAUJfpM9ZehVRvcm1g9euItE+AM=", + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "http://registry.npm.taobao.org/domutils/download/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "http://registry.npm.taobao.org/ecc-jsbn/download/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "entities": { + "version": "1.1.2", + "resolved": "http://registry.npm.taobao.org/entities/download/entities-1.1.2.tgz", + "integrity": "sha1-vfpzUplmTfr9NFKe1PhSKidf6lY=" + }, + "error-ex": { + "version": "1.3.2", + "resolved": "http://registry.npm.taobao.org/error-ex/download/error-ex-1.3.2.tgz", + "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "http://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "extend": { + "version": "3.0.2", + "resolved": "http://registry.npm.taobao.org/extend/download/extend-3.0.2.tgz", + "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "http://registry.npm.taobao.org/extsprintf/download/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "find-up": { + "version": "1.1.2", + "resolved": "http://registry.npm.taobao.org/find-up/download/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "http://registry.npm.taobao.org/forever-agent/download/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.1.4", + "resolved": "http://registry.npm.taobao.org/form-data/download/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.12" + } + }, + "generate-function": { + "version": "2.3.1", + "resolved": "http://registry.npm.taobao.org/generate-function/download/generate-function-2.3.1.tgz", + "integrity": "sha1-8GlhdpDBDIaOc7hGV0Z2T5fDR58=", + "requires": { + "is-property": "^1.0.2" + } + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "http://registry.npm.taobao.org/generate-object-property/download/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "requires": { + "is-property": "^1.0.0" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "http://registry.npm.taobao.org/get-caller-file/download/get-caller-file-1.0.3.tgz", + "integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "http://registry.npm.taobao.org/getpass/download/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/assert-plus/download/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "http://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.1.15.tgz", + "integrity": "sha1-/7cD4QZuig7qpMi4C6klPu77+wA=" + }, + "har-validator": { + "version": "2.0.6", + "resolved": "http://registry.npm.taobao.org/har-validator/download/har-validator-2.0.6.tgz", + "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", + "requires": { + "chalk": "^1.1.1", + "commander": "^2.9.0", + "is-my-json-valid": "^2.12.4", + "pinkie-promise": "^2.0.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "http://registry.npm.taobao.org/has-ansi/download/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "hawk": { + "version": "3.1.3", + "resolved": "http://registry.npm.taobao.org/hawk/download/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "requires": { + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "http://registry.npm.taobao.org/hoek/download/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "http://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.7.1.tgz", + "integrity": "sha1-l/I2l3vW4SVAiTD/bePuxigewEc=" + }, + "htmlparser2": { + "version": "3.10.0", + "resolved": "http://registry.npm.taobao.org/htmlparser2/download/htmlparser2-3.10.0.tgz", + "integrity": "sha1-X15CLc9hGcDZg+02Jgzp3tC+5GQ=", + "requires": { + "domelementtype": "^1.3.0", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.0.6" + } + }, + "http-signature": { + "version": "1.1.1", + "resolved": "http://registry.npm.taobao.org/http-signature/download/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "requires": { + "assert-plus": "^0.2.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "inherits": { + "version": "2.0.1", + "resolved": "http://registry.npm.taobao.org/inherits/download/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/invert-kv/download/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "http://registry.npm.taobao.org/is-arrayish/download/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/is-builtin-module/download/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-my-ip-valid": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/is-my-ip-valid/download/is-my-ip-valid-1.0.0.tgz", + "integrity": "sha1-ezUbjo7dTTmV1NBmaA5mTZRpaCQ=" + }, + "is-my-json-valid": { + "version": "2.19.0", + "resolved": "http://registry.npm.taobao.org/is-my-json-valid/download/is-my-json-valid-2.19.0.tgz", + "integrity": "sha1-j9bkA2PNBrlj+od9REv7Xt3GIXU=", + "requires": { + "generate-function": "^2.0.0", + "generate-object-property": "^1.1.0", + "is-my-ip-valid": "^1.0.0", + "jsonpointer": "^4.0.0", + "xtend": "^4.0.0" + } + }, + "is-property": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/is-property/download/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/is-typedarray/download/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "http://registry.npm.taobao.org/is-utf8/download/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "isarray": { + "version": "0.0.1", + "resolved": "http://registry.npm.taobao.org/isarray/download/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "http://registry.npm.taobao.org/isstream/download/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "http://registry.npm.taobao.org/jsbn/download/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "http://registry.npm.taobao.org/json-schema/download/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "http://registry.npm.taobao.org/json-stringify-safe/download/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "http://registry.npm.taobao.org/jsonpointer/download/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "http://registry.npm.taobao.org/jsprim/download/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/assert-plus/download/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/lcid/download/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "http://registry.npm.taobao.org/load-json-file/download/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "lodash.assignin": { + "version": "4.2.0", + "resolved": "http://registry.npm.taobao.org/lodash.assignin/download/lodash.assignin-4.2.0.tgz", + "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" + }, + "lodash.bind": { + "version": "4.2.1", + "resolved": "http://registry.npm.taobao.org/lodash.bind/download/lodash.bind-4.2.1.tgz", + "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "http://registry.npm.taobao.org/lodash.defaults/download/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "lodash.filter": { + "version": "4.6.0", + "resolved": "http://registry.npm.taobao.org/lodash.filter/download/lodash.filter-4.6.0.tgz", + "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=" + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "http://registry.npm.taobao.org/lodash.flatten/download/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, + "lodash.foreach": { + "version": "4.5.0", + "resolved": "http://registry.npm.taobao.org/lodash.foreach/download/lodash.foreach-4.5.0.tgz", + "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" + }, + "lodash.map": { + "version": "4.6.0", + "resolved": "http://registry.npm.taobao.org/lodash.map/download/lodash.map-4.6.0.tgz", + "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" + }, + "lodash.merge": { + "version": "4.6.1", + "resolved": "http://registry.npm.taobao.org/lodash.merge/download/lodash.merge-4.6.1.tgz", + "integrity": "sha1-rcJdnLmbk5HFliTzefu6YNcRHVQ=" + }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "http://registry.npm.taobao.org/lodash.pick/download/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" + }, + "lodash.reduce": { + "version": "4.6.0", + "resolved": "http://registry.npm.taobao.org/lodash.reduce/download/lodash.reduce-4.6.0.tgz", + "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" + }, + "lodash.reject": { + "version": "4.6.0", + "resolved": "http://registry.npm.taobao.org/lodash.reject/download/lodash.reject-4.6.0.tgz", + "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=" + }, + "lodash.some": { + "version": "4.6.0", + "resolved": "http://registry.npm.taobao.org/lodash.some/download/lodash.some-4.6.0.tgz", + "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" + }, + "log4js": { + "version": "1.1.1", + "resolved": "http://registry.npm.taobao.org/log4js/download/log4js-1.1.1.tgz", + "integrity": "sha1-wh0px2BAieTyVYM+f5SzRh3h/0M=", + "requires": { + "debug": "^2.2.0", + "semver": "^5.3.0", + "streamroller": "^0.4.0" + } + }, + "mime-db": { + "version": "1.37.0", + "resolved": "http://registry.npm.taobao.org/mime-db/download/mime-db-1.37.0.tgz", + "integrity": "sha1-C2oM5v2+lXbiXx8tL96IMNwK0Ng=" + }, + "mime-types": { + "version": "2.1.21", + "resolved": "http://registry.npm.taobao.org/mime-types/download/mime-types-2.1.21.tgz", + "integrity": "sha1-KJlaoey3cHQv5q5+WPkYHHRLP5Y=", + "requires": { + "mime-db": "~1.37.0" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "http://registry.npm.taobao.org/minimist/download/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "http://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "http://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "mustache": { + "version": "2.3.2", + "resolved": "http://registry.npm.taobao.org/mustache/download/mustache-2.3.2.tgz", + "integrity": "sha1-ptTZw/kdEzWauImoEpVPkjCj0MU=" + }, + "normalize-package-data": { + "version": "2.4.2", + "resolved": "http://registry.npm.taobao.org/normalize-package-data/download/normalize-package-data-2.4.2.tgz", + "integrity": "sha1-ayq9hXdOUfeTbxOV5FrLkF3ISbI=", + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/nth-check/download/nth-check-1.0.2.tgz", + "integrity": "sha1-sr0pXDfj3VijvwcAN2Zjuk2c8Fw=", + "requires": { + "boolbase": "~1.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/number-is-nan/download/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "http://registry.npm.taobao.org/oauth-sign/download/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "os-locale": { + "version": "1.4.0", + "resolved": "http://registry.npm.taobao.org/os-locale/download/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "http://registry.npm.taobao.org/parse-json/download/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "http://registry.npm.taobao.org/path-exists/download/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "http://registry.npm.taobao.org/path-type/download/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "http://registry.npm.taobao.org/pify/download/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "http://registry.npm.taobao.org/pinkie/download/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "http://registry.npm.taobao.org/pinkie-promise/download/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "http://registry.npm.taobao.org/punycode/download/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.3.2", + "resolved": "http://registry.npm.taobao.org/qs/download/qs-6.3.2.tgz", + "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=" + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "http://registry.npm.taobao.org/read-pkg/download/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/read-pkg-up/download/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "3.1.1", + "resolved": "http://registry.npm.taobao.org/readable-stream/download/readable-stream-3.1.1.tgz", + "integrity": "sha1-7Wu8bFuliwkAOf8YzmcFFXla6wY=", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "http://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "request": { + "version": "2.79.0", + "resolved": "http://registry.npm.taobao.org/request/download/request-2.79.0.tgz", + "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", + "requires": { + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "caseless": "~0.11.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.1.1", + "har-validator": "~2.0.6", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "oauth-sign": "~0.8.1", + "qs": "~6.3.0", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "~0.4.1", + "uuid": "^3.0.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "http://registry.npm.taobao.org/require-directory/download/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "http://registry.npm.taobao.org/require-main-filename/download/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "http://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "http://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz", + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" + }, + "semver": { + "version": "5.6.0", + "resolved": "http://registry.npm.taobao.org/semver/download/semver-5.6.0.tgz", + "integrity": "sha1-fnQlb7qknHWqfHogXMInmcrIAAQ=" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "http://registry.npm.taobao.org/set-blocking/download/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "sntp": { + "version": "1.0.9", + "resolved": "http://registry.npm.taobao.org/sntp/download/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "requires": { + "hoek": "2.x.x" + } + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "http://registry.npm.taobao.org/spdx-correct/download/spdx-correct-3.1.0.tgz", + "integrity": "sha1-+4PlBERSaPFUsHTiGMh8ADzTHfQ=", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "http://registry.npm.taobao.org/spdx-exceptions/download/spdx-exceptions-2.2.0.tgz", + "integrity": "sha1-LqRQrudPKom/uUUZwH/Nb0EyKXc=" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "http://registry.npm.taobao.org/spdx-expression-parse/download/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha1-meEZt6XaAOBUkcn6M4t5BII7QdA=", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.3", + "resolved": "http://registry.npm.taobao.org/spdx-license-ids/download/spdx-license-ids-3.0.3.tgz", + "integrity": "sha1-gcDOjyFHR1YUi7tfO/wPNr8V124=" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "http://registry.npm.taobao.org/sshpk/download/sshpk-1.16.1.tgz", + "integrity": "sha1-+2YcC+8ps520B2nuOfpwCT1vaHc=", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/assert-plus/download/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "streamroller": { + "version": "0.4.1", + "resolved": "http://registry.npm.taobao.org/streamroller/download/streamroller-0.4.1.tgz", + "integrity": "sha1-1DW9WXQ3Or2b2QaDWVEwhRBswF8=", + "requires": { + "date-format": "^0.0.0", + "debug": "^0.7.2", + "mkdirp": "^0.5.1", + "readable-stream": "^1.1.7" + }, + "dependencies": { + "debug": { + "version": "0.7.4", + "resolved": "http://registry.npm.taobao.org/debug/download/debug-0.7.4.tgz", + "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "http://registry.npm.taobao.org/readable-stream/download/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "http://registry.npm.taobao.org/string_decoder/download/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/string-width/download/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.2.0", + "resolved": "http://registry.npm.taobao.org/string_decoder/download/string_decoder-1.2.0.tgz", + "integrity": "sha1-/obnOLGVRK/nBGkkOyoe6SQOro0=", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringstream": { + "version": "0.0.6", + "resolved": "http://registry.npm.taobao.org/stringstream/download/stringstream-0.0.6.tgz", + "integrity": "sha1-eIAiWw1K0Q4wkn0Weh1vL9OzOnI=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npm.taobao.org/strip-ansi/download/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "http://registry.npm.taobao.org/strip-bom/download/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "http://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "thenify": { + "version": "3.3.0", + "resolved": "http://registry.npm.taobao.org/thenify/download/thenify-3.3.0.tgz", + "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "http://registry.npm.taobao.org/thenify-all/download/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "http://registry.npm.taobao.org/tough-cookie/download/tough-cookie-2.3.4.tgz", + "integrity": "sha1-7GDO44rGdQY//JelwYlwV47oNlU=", + "requires": { + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.4.3", + "resolved": "http://registry.npm.taobao.org/tunnel-agent/download/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=" + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "http://registry.npm.taobao.org/tweetnacl/download/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "unescape": { + "version": "0.2.0", + "resolved": "http://registry.npm.taobao.org/unescape/download/unescape-0.2.0.tgz", + "integrity": "sha1-t4ubYMhvFinfGBv1Pu47yNY2fd8=" + }, + "util": { + "version": "0.10.3", + "resolved": "http://registry.npm.taobao.org/util/download/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.3.2", + "resolved": "http://registry.npm.taobao.org/uuid/download/uuid-3.3.2.tgz", + "integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE=" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "http://registry.npm.taobao.org/validate-npm-package-license/download/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha1-/JH2uce6FchX9MssXe/uw51PQQo=", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "http://registry.npm.taobao.org/verror/download/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/assert-plus/download/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/which-module/download/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "http://registry.npm.taobao.org/wrap-ansi/download/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "http://registry.npm.taobao.org/xtend/download/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "y18n": { + "version": "3.2.1", + "resolved": "http://registry.npm.taobao.org/y18n/download/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yargs": { + "version": "6.6.0", + "resolved": "http://registry.npm.taobao.org/yargs/download/yargs-6.6.0.tgz", + "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^4.2.0" + } + }, + "yargs-parser": { + "version": "4.2.1", + "resolved": "http://registry.npm.taobao.org/yargs-parser/download/yargs-parser-4.2.1.tgz", + "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", + "requires": { + "camelcase": "^3.0.0" + } + } + } +} diff --git a/package.json b/package.json index 953a6a6..90bb7e3 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "lc-spider": "./bin/leetcode_spider" }, "scripts": { - "dev": "DEBUG=lc-spider lc-spider", + "debug": "DEBUG=lc-spider lc-spider", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { diff --git a/solutions/1160.find-words-that-can-be-formed-by-characters/find-words-that-can-be-formed-by-characters.java b/solutions/1160.find-words-that-can-be-formed-by-characters/find-words-that-can-be-formed-by-characters.java new file mode 100644 index 0000000..a04b51f --- /dev/null +++ b/solutions/1160.find-words-that-can-be-formed-by-characters/find-words-that-can-be-formed-by-characters.java @@ -0,0 +1,28 @@ +class Solution { + public int countCharacters(String[] words, String chars) { + int[] dictionary = new int[26]; + for(char ch:chars.toCharArray()){ + dictionary[ch-'a']++; + } + int res=0; + boolean learned=true; + int[] counter = new int[26]; + for(String w:words){ + learned=true; + for(char ch:w.toCharArray()){ + counter[ch-'a']++; + if(counter[ch-'a']>dictionary[ch-'a']){ + learned=false; + break; + } + } + if(learned){ + res+=w.length(); + } + for(int i=0;i<26;i++){ + counter[i]=0; + } + } + return res; + } +} \ No newline at end of file diff --git a/solutions/1160.find-words-that-can-be-formed-by-characters/question.md b/solutions/1160.find-words-that-can-be-formed-by-characters/question.md new file mode 100644 index 0000000..276f1e8 --- /dev/null +++ b/solutions/1160.find-words-that-can-be-formed-by-characters/question.md @@ -0,0 +1,35 @@ +You are given an array of strings words
and a string chars
.
A string is good if it can be formed by characters from chars
(each character can only be used once).
Return the sum of lengths of all good strings in words
.
+ +
Example 1:
+ ++Input: words = ["cat","bt","hat","tree"], chars = "atach" +Output: 6 +Explanation: +The strings that can be formed are "cat" and "hat" so the answer is 3 + 3 = 6. ++ +
Example 2:
+ ++Input: words = ["hello","world","leetcode"], chars = "welldonehoneyr" +Output: 10 +Explanation: +The strings that can be formed are "hello" and "world" so the answer is 5 + 5 = 10. ++ +
+ +
Note:
+ +1 <= words.length <= 1000
1 <= words[i].length, chars.length <= 100
Given an N x N grid
containing only values 0
and 1
, where 0
represents water and 1
represents land, find a water cell such that its distance to the nearest land cell is maximized and return the distance.
The distance used in this problem is the Manhattan distance: the distance between two cells (x0, y0)
and (x1, y1)
is |x0 - x1| + |y0 - y1|
.
If no land or water exists in the grid, return -1
.
+ +
Example 1:
+ ++Input: [[1,0,1],[0,0,0],[1,0,1]] +Output: 2 +Explanation: +The cell (1, 1) is as far as possible from all the land with distance 2. ++ +
Example 2:
+ ++Input: [[1,0,0],[0,0,0],[0,0,0]] +Output: 4 +Explanation: +The cell (2, 2) is as far as possible from all the land with distance 4. ++ +
+ +
Note:
+ +1 <= grid.length == grid[0].length <= 100
grid[i][j]
is 0
or 1
Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋
times.
You may assume that the array is non-empty and the majority element always exist in the array.
+ +Example 1:
+ ++Input: [3,2,3] +Output: 3+ +
Example 2:
+ ++Input: [2,2,1,1,1,2,2] +Output: 2 +diff --git a/solutions/300.longest-increasing-subsequence/longest-increasing-subsequence.java b/solutions/300.longest-increasing-subsequence/longest-increasing-subsequence.java new file mode 100644 index 0000000..afe5695 --- /dev/null +++ b/solutions/300.longest-increasing-subsequence/longest-increasing-subsequence.java @@ -0,0 +1,30 @@ +class Solution { + public int lengthOfLIS(int[] nums) { + if(nums.length<=1){ + return nums.length; + } + int[] tailOfSeqWithLength = new int[nums.length]; + int len=0; + for(int i:nums){ + if(len==0||i>tailOfSeqWithLength[len-1]){ + tailOfSeqWithLength[len++]=i; + }else{ + int left=0; + int right=len-1; + while(left<=right){ + int mid=left+(right-left)/2; + if(tailOfSeqWithLength[mid]Given an unsorted array of integers, find the length of longest increasing subsequence. + +
Example:
+ ++Input:+ +[10,9,2,5,3,7,101,18] +
Output: 4 +Explanation: The longest increasing subsequence is[2,3,7,101]
, therefore the length is4
.
Note:
+ +Follow up: Could you improve it to O(n log n) time complexity?
diff --git a/solutions/365.water-and-jug-problem/question.md b/solutions/365.water-and-jug-problem/question.md new file mode 100644 index 0000000..3800f0d --- /dev/null +++ b/solutions/365.water-and-jug-problem/question.md @@ -0,0 +1,25 @@ +You are given two jugs with capacities x and y litres. There is an infinite amount of water supply available. You need to determine whether it is possible to measure exactly z litres using these two jugs.
+ +If z liters of water is measurable, you must have z liters of water contained within one or both buckets by the end.
+ +Operations allowed:
+ +Example 1: (From the famous "Die Hard" example)
+ ++Input: x = 3, y = 5, z = 4 +Output: True ++ +
Example 2:
+ ++Input: x = 2, y = 6, z = 5 +Output: False +\ No newline at end of file diff --git a/solutions/365.water-and-jug-problem/water-and-jug-problem.java b/solutions/365.water-and-jug-problem/water-and-jug-problem.java new file mode 100644 index 0000000..da029e5 --- /dev/null +++ b/solutions/365.water-and-jug-problem/water-and-jug-problem.java @@ -0,0 +1,25 @@ +class Solution { + public boolean canMeasureWater(int x, int y, int z) { + if(x+y
Given a string which consists of lowercase or uppercase letters, find the length of the longest palindromes that can be built with those letters.
+ +This is case sensitive, for example "Aa"
is not considered a palindrome here.
Note:
+Assume the length of given string will not exceed 1,010.
+
Example: +
+Input: +"abccccdd" + +Output: +7 + +Explanation: +One longest palindrome that can be built is "dccaccd", whose length is 7. ++ \ No newline at end of file diff --git a/solutions/695.max-area-of-island/max-area-of-island.java b/solutions/695.max-area-of-island/max-area-of-island.java new file mode 100644 index 0000000..0a0eba3 --- /dev/null +++ b/solutions/695.max-area-of-island/max-area-of-island.java @@ -0,0 +1,25 @@ +class Solution { + public int maxAreaOfIsland(int[][] grid) { + int res=0; + if(grid.length==0){ + return 0; + } + for(int i=0;i
Given a non-empty 2D array grid
of 0's and 1's, an island is a group of 1
's (representing land) connected 4-directionally (horizontal or vertical.) You may assume all four edges of the grid are surrounded by water.
Find the maximum area of an island in the given 2D array. (If there is no island, the maximum area is 0.)
+ +Example 1:
+ ++[[0,0,1,0,0,0,0,1,0,0,0,0,0], + [0,0,0,0,0,0,0,1,1,1,0,0,0], + [0,1,1,0,1,0,0,0,0,0,0,0,0], + [0,1,0,0,1,1,0,0,1,0,1,0,0], + [0,1,0,0,1,1,0,0,1,1,1,0,0], + [0,0,0,0,0,0,0,0,0,0,1,0,0], + [0,0,0,0,0,0,0,1,1,1,0,0,0], + [0,0,0,0,0,0,0,1,1,0,0,0,0]] ++Given the above grid, return
6
. Note the answer is not 11, because the island must be connected 4-directionally.
+
+Example 2:
+ ++[[0,0,0,0,0,0,0,0]]+Given the above grid, return
0
.
+
+Note: The length of each dimension in the given grid
does not exceed 50.
Given a list of words, we may encode it by writing a reference string S
and a list of indexes A
.
For example, if the list of words is ["time", "me", "bell"]
, we can write it as S = "time#bell#"
and indexes = [0, 2, 5]
.
Then for each index, we will recover the word by reading from the reference string from that index until we reach a "#"
character.
What is the length of the shortest reference string S possible that encodes the given words?
+ +Example:
+ ++Input: words =+ +["time", "me", "bell"]
+Output: 10 +Explanation: S ="time#bell#" and indexes = [0, 2, 5
]. +
+ +
Note:
+ +1 <= words.length <= 2000
.1 <= words[i].length <= 7
.[x1, y1, x2, y2]
, where (x1, y1)
are the coordinates of its bottom-left corner, and (x2, y2)
are the coordinates of its top-right corner.
+
+Two rectangles overlap if the area of their intersection is positive. To be clear, two rectangles that only touch at the corner or edges do not overlap.
+ +Given two (axis-aligned) rectangles, return whether they overlap.
+ +Example 1:
+ ++Input: rec1 = [0,0,2,2], rec2 = [1,1,3,3] +Output: true ++ +
Example 2:
+ ++Input: rec1 = [0,0,1,1], rec2 = [1,0,2,1] +Output: false ++ +
Notes:
+ +rec1
and rec2
are lists of 4 integers.-10^9
and 10^9
.Given a non-empty, singly linked list with head node head
, return a middle node of linked list.
If there are two middle nodes, return the second middle node.
+ ++ +
Example 1:
+ ++Input: [1,2,3,4,5] +Output: Node 3 from this list (Serialization: [3,4,5]) +The returned node has value 3. (The judge's serialization of this node is [3,4,5]). +Note that we returned a ListNode object ans, such that: +ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, and ans.next.next.next = NULL. ++ +
Example 2:
+ ++Input: [1,2,3,4,5,6] +Output: Node 4 from this list (Serialization: [4,5,6]) +Since the list has two middle nodes with values 3 and 4, we return the second one. ++ +
+ +
Note:
+ +1
and 100
.On a N * N
grid, we place some 1 * 1 * 1
cubes.
Each value v = grid[i][j]
represents a tower of v
cubes placed on top of grid cell (i, j)
.
Return the total surface area of the resulting shapes.
+ ++ +
Example 1:
+ ++Input: [[2]] +Output: 10 ++ +
Example 2:
+ ++Input: [[1,2],[3,4]] +Output: 34 ++ +
Example 3:
+ ++Input: [[1,0],[0,2]] +Output: 16 ++ +
Example 4:
+ ++Input: [[1,1,1],[1,0,1],[1,1,1]] +Output: 32 ++ +
Example 5:
+ ++Input: [[2,2,2],[2,1,2],[2,2,2]] +Output: 46 ++ +
+ +
Note:
+ +1 <= N <= 50
0 <= grid[i][j] <= 50
In a deck of cards, each card has an integer written on it.
+ +Return true
if and only if you can choose X >= 2
such that it is possible to split the entire deck into 1 or more groups of cards, where:
X
cards.+
Example 1:
+ ++Input: deck = [1,2,3,4,4,3,2,1] +Output: true +Explanation: Possible partition [1,1],[2,2],[3,3],[4,4]. ++ +
Example 2:
+ ++Input: deck = [1,1,1,2,2,2,3,3] +Output: false´ +Explanation: No possible partition. ++ +
Example 3:
+ ++Input: deck = [1] +Output: false +Explanation: No possible partition. ++ +
Example 4:
+ ++Input: deck = [1,1] +Output: true +Explanation: Possible partition [1,1]. ++ +
Example 5:
+ ++Input: deck = [1,1,2,2,2,2] +Output: true +Explanation: Possible partition [1,1],[2,2],[2,2]. ++ +
+
Constraints:
+ +1 <= deck.length <= 10^4
0 <= deck[i] < 10^4
A[i]
, and incrementing it by 1
.
+
+Return the least number of moves to make every value in A
unique.
+ +
Example 1:
+ ++Input: [1,2,2] +Output: 1 +Explanation: After 1 move, the array could be [1, 2, 3]. ++ +
Example 2:
+ ++Input: [3,2,1,2,1,7] +Output: 6 +Explanation: After 6 moves, the array could be [3, 4, 1, 2, 5, 7]. +It can be shown with 5 or less moves that it is impossible for the array to have all unique values. ++ +
+
Note:
+ +0 <= A.length <= 40000
0 <= A[i] < 40000
On an 8 x 8 chessboard, there is one white rook. There also may be empty squares, white bishops, and black pawns. These are given as characters 'R', '.', 'B', and 'p' respectively. Uppercase characters represent white pieces, and lowercase characters represent black pieces.
+ +The rook moves as in the rules of Chess: it chooses one of four cardinal directions (north, east, west, and south), then moves in that direction until it chooses to stop, reaches the edge of the board, or captures an opposite colored pawn by moving to the same square it occupies. Also, rooks cannot move into the same square as other friendly bishops.
+ +Return the number of pawns the rook can capture in one move.
+ ++ +
Example 1:
+ ++Input: [[".",".",".",".",".",".",".","."],[".",".",".","p",".",".",".","."],[".",".",".","R",".",".",".","p"],[".",".",".",".",".",".",".","."],[".",".",".",".",".",".",".","."],[".",".",".","p",".",".",".","."],[".",".",".",".",".",".",".","."],[".",".",".",".",".",".",".","."]] +Output: 3 +Explanation: +In this example the rook is able to capture all the pawns. ++ +
Example 2:
+ ++Input: [[".",".",".",".",".",".",".","."],[".","p","p","p","p","p",".","."],[".","p","p","B","p","p",".","."],[".","p","B","R","B","p",".","."],[".","p","p","B","p","p",".","."],[".","p","p","p","p","p",".","."],[".",".",".",".",".",".",".","."],[".",".",".",".",".",".",".","."]] +Output: 0 +Explanation: +Bishops are blocking the rook to capture any pawn. ++ +
Example 3:
+ ++Input: [[".",".",".",".",".",".",".","."],[".",".",".","p",".",".",".","."],[".",".",".","p",".",".",".","."],["p","p",".","R",".","p","B","."],[".",".",".",".",".",".",".","."],[".",".",".","B",".",".",".","."],[".",".",".","p",".",".",".","."],[".",".",".",".",".",".",".","."]] +Output: 3 +Explanation: +The rook can capture the pawns at positions b5, d6 and f5. ++ +
+ +
Note:
+ +board.length == board[i].length == 8
board[i][j]
is either 'R'
, '.'
, 'B'
, or 'p'
board[i][j] == 'R'
Implement a method to perform basic string compression using the counts of repeated characters. For example, the string aabcccccaaa would become a2blc5a3. If the "compressed" string would not become smaller than the original string, your method should return the original string. You can assume the string has only uppercase and lowercase letters (a - z).
+ +Example 1:
+ ++Input: "aabcccccaaa" +Output: "a2b1c5a3" ++ +
Example 2:
+ ++Input: "abbccd" +Output: "abbccd" +Explanation: +The compressed string is "a1b2c2d1", which is longer than the original string. ++ +
+ +
Note:
+ +0 <= S.length <= 50000
A popular masseuse receives a sequence of back-to-back appointment requests and is debating which ones to accept. She needs a break between appointments and therefore she cannot accept any adjacent requests. Given a sequence of back-to-back appoint ment requests, find the optimal (highest total booked minutes) set the masseuse can honor. Return the number of minutes.
+ +Note: This problem is slightly different from the original one in the book.
+ ++ +
Example 1:
+ ++Input: [1,2,3,1] +Output: 4 +Explanation: Accept request 1 and 3, total minutes = 1 + 3 = 4 ++ +
Example 2:
+ ++Input: [2,7,9,3,1] +Output: 12 +Explanation: Accept request 1, 3 and 5, total minutes = 2 + 9 + 1 = 12 ++ +
Example 3:
+ ++Input: [2,1,4,5,3,1,1,3] +Output: 12 +Explanation: Accept request 1, 3, 5 and 8, total minutes = 2 + 4 + 3 + 3 = 12 +diff --git "a/solutions/\351\235\242\350\257\225\351\242\230 17.16.the-masseuse-lcci/the-masseuse-lcci.java" "b/solutions/\351\235\242\350\257\225\351\242\230 17.16.the-masseuse-lcci/the-masseuse-lcci.java" new file mode 100644 index 0000000..3105fb4 --- /dev/null +++ "b/solutions/\351\235\242\350\257\225\351\242\230 17.16.the-masseuse-lcci/the-masseuse-lcci.java" @@ -0,0 +1,16 @@ +class Solution { + public int massage(int[] nums) { + int[] endOf=new int[nums.length]; + if(nums.length==0){ + return 0; + }else if(nums.length==1){ + return nums[0]; + } + endOf[0]=nums[0]; + endOf[1]=Math.max(nums[0],nums[1]); + for(int i=2;i