Skip to content

Commit 6eef509

Browse files
committed
Use the real validator for the hosted version in the docs.
1 parent 976035a commit 6eef509

File tree

5 files changed

+327
-357
lines changed

5 files changed

+327
-357
lines changed

grunt.js

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ grunt.initConfig({
3434
"build-pages": {
3535
all: grunt.file.expandFiles( "pages/**" )
3636
},
37-
"build-resources": {
38-
all: grunt.file.expandFiles( "resources/**/*" )
39-
},
37+
"build-resources": {
38+
all: grunt.file.expandFiles( "resources/**" )
39+
},
4040
wordpress: grunt.utils._.extend({
4141
dir: "dist/wordpress"
4242
}, config.wordpress )
@@ -59,23 +59,54 @@ grunt.registerHelper( "wordpress-get-postpaths", function( fn ) {
5959
});
6060
});
6161

62+
grunt.registerMultiTask( "build-resources", "Copy resources", function() {
63+
var task = this,
64+
taskDone = task.async(),
65+
files = this.data,
66+
targetDir = grunt.config( "wordpress.dir" ) + "/resources/";
67+
68+
grunt.file.mkdir( targetDir );
69+
70+
grunt.utils.async.forEachSeries( files, function( fileName, fileDone ) {
71+
grunt.file.copy( fileName, targetDir + fileName.replace( /^.+?\//, "" ) );
72+
fileDone();
73+
}, function() {
74+
if ( task.errorCount ) {
75+
grunt.warn( "Error building resources." );
76+
return taskDone( false );
77+
}
78+
79+
grunt.log.writeln( "Built " + files.length + " resources." );
80+
81+
// Build validate.js
82+
grunt.file.write( targetDir + "/validate.js",
83+
"(function() {" +
84+
grunt.file.read( require.resolve( "semver" ) ) + ";" +
85+
grunt.file.read( "lib/manifest.js" ) +
86+
grunt.file.read( "resources/validate.js" ) +
87+
"})();" );
88+
89+
taskDone();
90+
});
91+
});
92+
6293
grunt.registerTask( "sync-docs", function() {
63-
var done = this.async();
64-
var dir = grunt.config( "wordpress.dir" );
65-
66-
async.waterfall([
67-
function syncPosts( fn ) {
68-
grunt.helper( "wordpress-sync-posts", "dist/wordpress/posts/", fn );
69-
},
70-
function syncResources( fn ) {
71-
grunt.helper( "wordpress-sync-resources", path.join( dir, "resources/" ), fn );
72-
}
73-
], function( error ) {
74-
if ( !error ) {
75-
return done();
94+
var done = this.async(),
95+
dir = grunt.config( "wordpress.dir" );
96+
97+
async.waterfall([
98+
function syncPosts( fn ) {
99+
grunt.helper( "wordpress-sync-posts", path.join( dir, "posts/" ), fn );
100+
},
101+
function syncResources( fn ) {
102+
grunt.helper( "wordpress-sync-resources", path.join( dir, "resources/" ), fn );
103+
}
104+
], function( error ) {
105+
if ( error ) {
106+
return done( false );
76107
}
77108

78-
done( false );
109+
done();
79110
});
80111
});
81112

@@ -148,9 +179,11 @@ grunt.registerTask( "restore-repos", function() {
148179
});
149180

150181

182+
151183
grunt.registerTask( "default", "lint test" );
152-
grunt.registerTask( "setup", "setup-pluginsdb setup-retrydb build-pages sync-docs build-resources" );
153-
grunt.registerTask( "update", "clean build-pages build-resources sync-docs" );
154-
grunt.registerTask( "restore", "clean-retries setup-retrydb build-pages sync-docs build-resources restore-repos" );
184+
grunt.registerTask( "publish-docs", "build-pages build-resources sync-docs" );
185+
grunt.registerTask( "setup", "setup-pluginsdb setup-retrydb publish-docs" );
186+
grunt.registerTask( "update", "clean publish-docs" );
187+
grunt.registerTask( "restore", "clean-retries setup-retrydb publish-docs restore-repos" );
155188

156189
};

lib/manifest.js

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
(function ( exports, semver ) {
2+
3+
exports.blacklist = [];
4+
exports.suites = [];
5+
6+
function isObject( obj ) {
7+
return ({}).toString.call( obj ) === "[object Object]";
8+
}
9+
10+
function isUrl( /*str*/ ) {
11+
// TODO: URL validation
12+
return true;
13+
}
14+
15+
function isEmail( str ) {
16+
return (/^[a-zA-Z0-9.!#$%&'*+\/=?\^_`{|}~\-]+@[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*$/).test( str );
17+
}
18+
19+
exports.validate = function( manifest, version, prefix, filename ) {
20+
var errors = [];
21+
22+
/** required fields **/
23+
24+
if ( !manifest.name ) {
25+
errors.push( "Missing required field: name." );
26+
} else if ( typeof manifest.name !== "string" ) {
27+
errors.push( "Invalid data type for name; must be a string." );
28+
} else if ( !(/^[a-zA-Z0-9_\.\-]+$/).test( manifest.name ) ) {
29+
errors.push( "Name contains invalid characters." );
30+
} else if ( exports.blacklist.indexOf( manifest.name ) !== -1 ) {
31+
errors.push( "Name must not be '" + manifest.name + "'." );
32+
} else {
33+
if ( prefix ) {
34+
if ( manifest.name.indexOf( prefix ) !== 0 ) {
35+
errors.push( "Name must start with '" + prefix + "'." );
36+
}
37+
} else {
38+
Object.keys( exports.suites ).forEach(function( repoId ) {
39+
var prefix = exports.suites[ repoId ];
40+
if ( manifest.name.indexOf( prefix ) === 0 &&
41+
!(/\./).test( manifest.name.substr( prefix.length ) ) ) {
42+
errors.push( "Name must not start with '" + prefix + "'." );
43+
}
44+
});
45+
}
46+
47+
if ( filename && filename.substr( 0, filename.length - 12 ) !== manifest.name ) {
48+
errors.push( "Name must match manifest file name." );
49+
}
50+
}
51+
52+
if ( !manifest.version ) {
53+
errors.push( "Missing required field: version." );
54+
} else if ( typeof manifest.version !== "string" ) {
55+
errors.push( "Invalid data type for version; must be a string." );
56+
} else if ( manifest.version !== semver.clean( manifest.version ) ) {
57+
errors.push( "Manifest version (" + manifest.version + ") is invalid." );
58+
// version may not be provided when run as a standalone validator
59+
} else if ( version && (manifest.version !== semver.clean( version )) ) {
60+
errors.push( "Manifest version (" + manifest.version + ") does not match tag (" + version + ")." );
61+
}
62+
63+
if ( !manifest.title ) {
64+
errors.push( "Missing required field: title." );
65+
} else if ( typeof manifest.title !== "string" ) {
66+
errors.push( "Invalid data type for title; must be a string." );
67+
}
68+
69+
if ( !manifest.author ) {
70+
errors.push( "Missing required field: author." );
71+
} else if ( !isObject( manifest.author ) ) {
72+
errors.push( "Invalid data type for author; must be an object." );
73+
} else if ( !manifest.author.name ) {
74+
errors.push( "Missing required field: author.name." );
75+
} else {
76+
if ( typeof manifest.author.name !== "string" ) {
77+
errors.push( "Invalid data type for author.name; must be a string." );
78+
}
79+
80+
if ( "email" in manifest.author ) {
81+
if ( typeof manifest.author.email !== "string" ) {
82+
errors.push( "Invalid data type for author.email; must be a string." );
83+
} else if ( !isEmail( manifest.author.email ) ) {
84+
errors.push( "Invalid value for author.email." );
85+
}
86+
}
87+
88+
if ( "url" in manifest.author ) {
89+
if ( typeof manifest.author.url !== "string" ) {
90+
errors.push( "Invalid data type for author.url; must be a string." );
91+
} else if ( !isUrl( manifest.author.url ) ) {
92+
errors.push( "Invalid value for author.url." );
93+
}
94+
}
95+
}
96+
97+
if ( !manifest.licenses ) {
98+
errors.push( "Missing required field: licenses." );
99+
} else if ( !Array.isArray( manifest.licenses ) ) {
100+
errors.push( "Invalid data type for licenses; must be an array." );
101+
} else if ( !manifest.licenses.length ) {
102+
errors.push( "There must be at least one license." );
103+
} else {
104+
manifest.licenses.forEach(function( license, i ) {
105+
if ( !license.url ) {
106+
errors.push( "Missing required field: licenses[" + i + "].url." );
107+
} else if ( typeof license.url !== "string" ) {
108+
errors.push( "Invalid data type for licenses[" + i + "].url; must be a string." );
109+
} else if ( !isUrl( license.url ) ) {
110+
errors.push( "Invalid value for license.url." );
111+
}
112+
});
113+
}
114+
115+
if ( !manifest.dependencies ) {
116+
errors.push( "Missing required field: dependencies." );
117+
} else if ( !isObject( manifest.dependencies ) ) {
118+
errors.push( "Invalid data type for dependencies; must be an object." );
119+
} else {
120+
if ( !manifest.dependencies.jquery ) {
121+
errors.push( "Missing required dependency: jquery." );
122+
}
123+
Object.keys( manifest.dependencies ).forEach(function( dependency ) {
124+
if ( typeof manifest.dependencies[ dependency ] !== "string" ) {
125+
errors.push( "Invalid data type for dependencies[" + dependency + "];" +
126+
" must be a string." );
127+
} else if ( semver.validRange( manifest.dependencies[ dependency ] ) === null ) {
128+
errors.push( "Invalid version range for dependency: " + dependency + "." );
129+
}
130+
});
131+
}
132+
133+
/** optional fields **/
134+
135+
if ( "description" in manifest && typeof manifest.description !== "string" ) {
136+
errors.push( "Invalid data type for description; must be a string." );
137+
}
138+
139+
if ( "keywords" in manifest ) {
140+
if ( !Array.isArray( manifest.keywords ) ) {
141+
errors.push( "Invalid data type for keywords; must be an array." );
142+
} else {
143+
manifest.keywords.forEach(function( keyword, i ) {
144+
if ( typeof keyword !== "string" ) {
145+
errors.push( "Invalid data type for keywords[" + i + "]; must be a string." );
146+
} else if ( !(/^[a-zA-Z0-9\.\-]+$/).test( keyword ) ) {
147+
errors.push( "Invalid characters for keyword: " + keyword + "." );
148+
}
149+
});
150+
}
151+
}
152+
153+
if ( "homepage" in manifest ) {
154+
if ( typeof manifest.homepage !== "string" ) {
155+
errors.push( "Invalid data type for homepage; must be a string." );
156+
} else if ( !isUrl( manifest.homepage ) ) {
157+
errors.push( "Invalid value for homepage." );
158+
}
159+
}
160+
161+
if ( "docs" in manifest ) {
162+
if ( typeof manifest.docs !== "string" ) {
163+
errors.push( "Invalid data type for docs; must be a string." );
164+
} else if ( !isUrl( manifest.docs ) ) {
165+
errors.push( "Invalid value for docs." );
166+
}
167+
}
168+
169+
if ( "demo" in manifest ) {
170+
if ( typeof manifest.demo !== "string" ) {
171+
errors.push( "Invalid data type for demo; must be a string." );
172+
} else if ( !isUrl( manifest.demo ) ) {
173+
errors.push( "Invalid value for demo." );
174+
}
175+
}
176+
177+
if ( "download" in manifest ) {
178+
if ( typeof manifest.download !== "string" ) {
179+
errors.push( "Invalid data type for download; must be a string." );
180+
} else if ( !isUrl( manifest.download ) ) {
181+
errors.push( "Invalid value for download." );
182+
}
183+
}
184+
185+
if ( "bugs" in manifest ) {
186+
// check { url: "..." } format
187+
if ( typeof manifest.bugs === "object" ) {
188+
if ( typeof manifest.bugs.url !== "string" ) {
189+
errors.push( "Invalid data type for bugs.url; must be a string." );
190+
} else if ( !isUrl( manifest.bugs.url ) ) {
191+
errors.push( "Invalid value for bugs.url." );
192+
}
193+
} else {
194+
if ( typeof manifest.bugs !== "string" ) {
195+
errors.push( "Invalid data type for bugs; must be a string." );
196+
} else if ( !isUrl( manifest.bugs ) ) {
197+
errors.push( "Invalid value for bugs." );
198+
}
199+
}
200+
}
201+
202+
if ( "maintainers" in manifest ) {
203+
if ( !Array.isArray( manifest.maintainers ) ) {
204+
errors.push( "Invalid data type for maintainers; must be an array." );
205+
} else {
206+
manifest.maintainers.forEach(function( maintainer, i ) {
207+
if ( !isObject( maintainer ) ) {
208+
errors.push( "Invalid data type for maintainers[" + i + "]; must be an object." );
209+
return;
210+
}
211+
212+
if ( !("name" in maintainer) ) {
213+
errors.push( "Missing required field: maintainers[" + i + "].name." );
214+
} else if ( typeof maintainer.name !== "string" ) {
215+
errors.push( "Invalid data type for maintainers[" + i + "].name; must be a string." );
216+
}
217+
218+
if ( "email" in maintainer ) {
219+
if ( typeof maintainer.email !== "string" ) {
220+
errors.push( "Invalid data type for maintainers[" + i + "].email; must be a string." );
221+
} else if ( !isEmail( maintainer.email ) ) {
222+
errors.push( "Invalid value for maintainers[" + i + "].email." );
223+
}
224+
}
225+
226+
if ( "url" in maintainer ) {
227+
if ( typeof maintainer.url !== "string" ) {
228+
errors.push( "Invalid data type for maintainers[" + i + "].url; must be a string." );
229+
} else if ( !isUrl( maintainer.url ) ) {
230+
errors.push( "Invalid value for maintainers[" + i + "].url." );
231+
}
232+
}
233+
});
234+
}
235+
}
236+
237+
return errors;
238+
};
239+
240+
})(
241+
typeof exports === "object" ? exports : this.Manifest = {},
242+
this.semver || require( "semver" )
243+
);

0 commit comments

Comments
 (0)