Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.

Commit b99fe36

Browse files
author
Narciso Jaramillo
committed
Merge pull request #3261 from adobe/dangoor/shared-extensibility
Breaks out package validation for sharing with brackets-registry
2 parents 4a85d7a + 2b78ad6 commit b99fe36

File tree

6 files changed

+262
-189
lines changed

6 files changed

+262
-189
lines changed

src/extensibility/Package.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,15 @@ define(function (require, exports, module) {
7878
* if there are errors or null if the extension did not include package.json.
7979
*
8080
* @param {string} Absolute path to the package zip file
81+
* @param {{requirePackageJSON: ?boolean}} validation options
8182
* @return {$.Promise} A promise that is resolved with information about the package
8283
*/
83-
function validate(path) {
84+
function validate(path, options) {
8485
var d = new $.Deferred();
8586
_nodeConnectionDeferred
8687
.done(function (nodeConnection) {
8788
if (nodeConnection.connected()) {
88-
nodeConnection.domains.extensionManager.validate(path)
89+
nodeConnection.domains.extensionManager.validate(path, options)
8990
.done(function (result) {
9091
d.resolve({
9192
errors: result.errors,

src/extensibility/node/ExtensionManagerDomain.js

Lines changed: 15 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -27,25 +27,18 @@ indent: 4, maxerr: 50 */
2727

2828
"use strict";
2929

30-
var unzip = require("unzip"),
31-
semver = require("semver"),
32-
path = require("path"),
33-
http = require("http"),
34-
request = require("request"),
35-
os = require("os"),
36-
fs = require("fs-extra");
30+
var unzip = require("unzip"),
31+
semver = require("semver"),
32+
path = require("path"),
33+
http = require("http"),
34+
request = require("request"),
35+
os = require("os"),
36+
fs = require("fs-extra"),
37+
validate = require("./package-validator").validate;
3738

3839

3940
var Errors = {
40-
NOT_FOUND_ERR: "NOT_FOUND_ERR", // {0} is path where ZIP file was expected
41-
INVALID_ZIP_FILE: "INVALID_ZIP_FILE", // {0} is path to ZIP file
42-
INVALID_PACKAGE_JSON: "INVALID_PACKAGE_JSON", // {0} is JSON parse error, {1} is path to ZIP file
43-
MISSING_PACKAGE_NAME: "MISSING_PACKAGE_NAME", // {0} is path to ZIP file
44-
BAD_PACKAGE_NAME: "BAD_PACKAGE_NAME", // {0} is the name
45-
MISSING_PACKAGE_VERSION: "MISSING_PACKAGE_VERSION", // {0} is path to ZIP file
46-
INVALID_VERSION_NUMBER: "INVALID_VERSION_NUMBER", // {0} is version string in JSON, {1} is path to ZIP file
4741
API_NOT_COMPATIBLE: "API_NOT_COMPATIBLE",
48-
MISSING_MAIN: "MISSING_MAIN", // {0} is path to ZIP file
4942
MISSING_REQUIRED_OPTIONS: "MISSING_REQUIRED_OPTIONS",
5043
ALREADY_INSTALLED: "ALREADY_INSTALLED",
5144
DOWNLOAD_ID_IN_USE: "DOWNLOAD_ID_IN_USE",
@@ -62,162 +55,6 @@ var Errors = {
6255
*/
6356
var pendingDownloads = {};
6457

65-
/**
66-
* Returns true if the name presented is acceptable as a package name. This enforces the
67-
* requirement as presented in the CommonJS spec: http://wiki.commonjs.org/wiki/Packages/1.0
68-
*
69-
* @param {string} Name to test
70-
* @return {boolean} true if the name is valid
71-
*/
72-
function validateName(name) {
73-
// "This must be a unique, lowercase alpha-numeric name without spaces. It may include "." or "_" or "-" characters."
74-
if (/^[a-z0-9._\-]+$/.exec(name)) {
75-
return true;
76-
}
77-
return false;
78-
}
79-
80-
/**
81-
* Implements the "validate" command in the "extensions" domain.
82-
* Validates the zipped package at path.
83-
*
84-
* The "err" parameter of the callback is only set if there was an
85-
* unexpected error. Otherwise, errors are reported in the result.
86-
*
87-
* The result object has an "errors" property. It is an array of
88-
* arrays of strings. Each array in the array is a set of parameters
89-
* that can be passed to StringUtils.format for internationalization.
90-
* The array will be empty if there are no errors.
91-
*
92-
* The result will have a "metadata" property if the metadata was
93-
* read successfully from package.json in the zip file.
94-
*
95-
* @param {string} Absolute path to the package zip file
96-
* @param {function} callback (err, result)
97-
*/
98-
function _cmdValidate(path, callback) {
99-
fs.exists(path, function (doesExist) {
100-
if (!doesExist) {
101-
callback(null, {
102-
errors: [[Errors.NOT_FOUND_ERR, path]]
103-
});
104-
return;
105-
}
106-
var callbackCalled = false;
107-
var metadata;
108-
var foundMainIn = null;
109-
var errors = [];
110-
var commonPrefix = null;
111-
112-
var readStream = fs.createReadStream(path);
113-
114-
readStream
115-
.pipe(unzip.Parse())
116-
.on("error", function (exception) {
117-
// General error to report for problems reading the file
118-
errors.push([Errors.INVALID_ZIP_FILE, path]);
119-
callback(null, {
120-
errors: errors
121-
});
122-
callbackCalled = true;
123-
readStream.destroy();
124-
})
125-
.on("entry", function (entry) {
126-
// look for the metadata
127-
var fileName = entry.path;
128-
129-
var slash = fileName.indexOf("/");
130-
if (slash > -1) {
131-
var prefix = fileName.substring(0, slash);
132-
if (commonPrefix === null) {
133-
commonPrefix = prefix;
134-
} else if (prefix !== commonPrefix) {
135-
commonPrefix = "";
136-
}
137-
if (commonPrefix) {
138-
fileName = fileName.substring(commonPrefix.length + 1);
139-
}
140-
} else {
141-
commonPrefix = "";
142-
}
143-
144-
if (fileName === "package.json") {
145-
// This handles an edge case where we found a package.json in a
146-
// nested directory that we thought was a commonPrefix but
147-
// actually wasn't. We reset as if we never read that first
148-
// package.json
149-
if (metadata) {
150-
metadata = undefined;
151-
errors = [];
152-
}
153-
154-
var packageJSON = "";
155-
entry
156-
.on("data", function (data) {
157-
// We're assuming utf8 encoding here, which is pretty safe
158-
// Note that I found that .setEncoding on the stream
159-
// would fail, so I convert the buffer to a string here.
160-
packageJSON += data.toString("utf8");
161-
})
162-
.on("error", function (exception) {
163-
// general exception handler. It is unknown what kinds of
164-
// errors we can get here.
165-
callback(exception, null);
166-
callbackCalled = true;
167-
readStream.destroy();
168-
})
169-
.on("end", function () {
170-
// attempt to parse the metadata
171-
try {
172-
metadata = JSON.parse(packageJSON);
173-
} catch (e) {
174-
errors.push([Errors.INVALID_PACKAGE_JSON, e.toString(), path]);
175-
return;
176-
}
177-
178-
// confirm required fields in the metadata
179-
if (!metadata.name) {
180-
errors.push([Errors.MISSING_PACKAGE_NAME, path]);
181-
} else if (!validateName(metadata.name)) {
182-
errors.push([Errors.BAD_PACKAGE_NAME, metadata.name]);
183-
}
184-
if (!metadata.version) {
185-
errors.push([Errors.MISSING_PACKAGE_VERSION, path]);
186-
} else if (!semver.valid(metadata.version)) {
187-
errors.push([Errors.INVALID_VERSION_NUMBER, metadata.version, path]);
188-
}
189-
});
190-
} else if (fileName === "main.js") {
191-
foundMainIn = commonPrefix;
192-
}
193-
})
194-
.on("end", function () {
195-
// Reached the end of the zipfile
196-
// Report results
197-
198-
// generally, if we hit an exception, we've already called the callback
199-
if (callbackCalled) {
200-
return;
201-
}
202-
203-
if (foundMainIn === null || foundMainIn !== commonPrefix) {
204-
errors.push([Errors.MISSING_MAIN, path]);
205-
}
206-
207-
// No errors and no metadata means that we never found the metadata
208-
if (errors.length === 0 && !metadata) {
209-
metadata = null;
210-
}
211-
212-
callback(null, {
213-
errors: errors,
214-
metadata: metadata,
215-
commonPrefix: commonPrefix
216-
});
217-
});
218-
});
219-
}
220-
22158

22259
/**
22360
* Private function to unzip to the correct directory.
@@ -387,7 +224,7 @@ function _cmdInstall(packagePath, destinationDirectory, options, callback) {
387224
}
388225
};
389226

390-
_cmdValidate(packagePath, validateCallback);
227+
validate(packagePath, {}, validateCallback);
391228
}
392229

393230

@@ -511,13 +348,17 @@ function init(domainManager) {
511348
domainManager.registerCommand(
512349
"extensionManager",
513350
"validate",
514-
_cmdValidate,
351+
validate,
515352
true,
516353
"Verifies that the contents of the given ZIP file are a valid Brackets extension package",
517354
[{
518355
name: "path",
519356
type: "string",
520357
description: "absolute filesystem path of the extension package"
358+
}, {
359+
name: "options",
360+
type: "{requirePackageJSON: ?boolean}",
361+
description: "options to control the behavior of the validator"
521362
}],
522363
[{
523364
name: "errors",
@@ -609,7 +450,7 @@ function init(domainManager) {
609450
}
610451

611452
// used in unit tests
612-
exports._cmdValidate = _cmdValidate;
453+
exports._cmdValidate = validate;
613454
exports._cmdInstall = _cmdInstall;
614455

615456
// used to load the domain

src/extensibility/node/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## Brackets Extensibility
2+
3+
This code is used by Brackets to implement its extension management features. It is likely not useful to anyone not working with Brackets extensions.

0 commit comments

Comments
 (0)