UNPKG

6.45 kBJavaScriptView Raw
1/**
2 * require and specify a consistent component name pattern
3 *
4 * All your file names should match the angular component name.
5 * The second parameter can be a config object [2, {nameStyle: 'dash', typeSeparator: 'dot', ignoreTypeSuffix: true, ignorePrefix: 'ui'}] to match 'avenger-profile.directive.js' or 'avanger-api.service.js'.
6 * Possible values for 'typeSeparator' and 'nameStyle' are 'dot', 'dash' and 'underscore'.
7 * The options 'ignoreTypeSuffix' ignores camel cased suffixes like 'someController' or 'myService' and 'ignorePrefix' ignores namespace prefixes like 'ui'.
8 *
9 * The naming scheme is <componentName><typeSeparator><componentType>.js
10 *
11 * The *componentType* for all service types (service, factory, provider, value) is 'service'.
12 * Since 1.5.0 it is possible to configure custom mappings for the *componentType*: {typeSeparator: 'dot', componentTypeMappings: {factory: 'factory', provider: 'provider'}.
13 *
14 * @styleguideReference {johnpapa} `y120` Naming - Naming Guidelines
15 * @styleguideReference {johnpapa} `y121` Naming - Feature File Names
16 * @version 0.7.0
17 * @category naming
18 * @sinceAngularVersion 1.x
19 */
20'use strict';
21
22var path = require('path');
23
24var utils = require('./utils/utils');
25
26module.exports = {
27 meta: {
28 schema: [{
29 type: ['object']
30 }]
31 },
32 create: (function() {
33 var fileEnding = '.js';
34
35 var separators = {
36 dot: '.',
37 dash: '-',
38 underscore: '_'
39 };
40
41 function createComponentTypeMappings(options) {
42 var componentTypeMappingOptions = options.componentTypeMappings || {};
43
44 return {
45 module: componentTypeMappingOptions.module || 'module',
46 controller: componentTypeMappingOptions.controller || 'controller',
47 directive: componentTypeMappingOptions.directive || 'directive',
48 filter: componentTypeMappingOptions.filter || 'filter',
49 service: componentTypeMappingOptions.service || 'service',
50 factory: componentTypeMappingOptions.factory || 'service',
51 provider: componentTypeMappingOptions.provider || 'service',
52 value: componentTypeMappingOptions.value || 'service',
53 constant: componentTypeMappingOptions.constant || 'constant',
54 component: componentTypeMappingOptions.component || 'component'
55 };
56 }
57
58 var filenameUtil = {
59 firstToUpper: function(value) {
60 return value[0].toUpperCase() + value.slice(1);
61 },
62 firstToLower: function(value) {
63 return value[0].toLowerCase() + value.slice(1);
64 },
65 removeTypeSuffix: function(name, type) {
66 var nameTypeLengthDiff = name.length - type.length;
67 if (nameTypeLengthDiff <= 0) {
68 return name;
69 }
70 var typeCamelCase = this.firstToUpper(type);
71 if (name.indexOf(typeCamelCase) === nameTypeLengthDiff) {
72 return name.slice(0, nameTypeLengthDiff);
73 }
74 return name;
75 },
76 removePrefix: function(name, options) {
77 var regName = '^' + options.ignorePrefix.replace(/[\.]/g, '\\$&');
78 regName += options.ignorePrefix.indexOf('\.') === -1 ? '[A-Z]' : '[a-zA-z]';
79 if (new RegExp(regName).test(name)) {
80 return this.firstToLower(name.slice(options.ignorePrefix.length));
81 }
82 return name;
83 },
84 transformComponentName: function(name, options) {
85 var nameStyle = options.nameStyle;
86 var nameSeparator = separators[nameStyle];
87 if (nameSeparator) {
88 var replacement = '$1' + nameSeparator + '$2';
89 name = name.replace(/([a-z0-9])([A-Z])/g, replacement).toLowerCase();
90 }
91 return name;
92 },
93 createExpectedName: function(name, type, options) {
94 var typeSeparator = separators[options.typeSeparator];
95
96 if (options.ignoreTypeSuffix) {
97 name = filenameUtil.removeTypeSuffix(name, type);
98 }
99 if (options.ignorePrefix && options.ignorePrefix.length > 0) {
100 name = filenameUtil.removePrefix(name, options);
101 }
102 if (options.nameStyle) {
103 name = filenameUtil.transformComponentName(name, options);
104 }
105 if (typeSeparator !== undefined) {
106 name = name + typeSeparator + type;
107 }
108 if (options.casing === 'camel') {
109 name = filenameUtil.firstToLower(name);
110 }
111 if (options.casing === 'pascal') {
112 name = filenameUtil.firstToUpper(name);
113 }
114 return name + fileEnding;
115 }
116 };
117
118 return function(context) {
119 var options = context.options[0] || {};
120 var filename = path.basename(context.getFilename());
121 var componentTypeMappings = createComponentTypeMappings(options);
122
123 return {
124 CallExpression: function(node) {
125 if ((utils.isAngularComponent(node) || utils.isAngularComponentDeclaration(node)) && utils.isMemberExpression(node.callee)) {
126 var name = node.arguments[0].value;
127 var type = componentTypeMappings[node.callee.property.name];
128 var expectedName;
129
130 if (type === undefined || (type === 'service' && node.callee.object.name === '$provide')) {
131 return;
132 }
133
134 if (!name) {
135 return;
136 }
137 expectedName = filenameUtil.createExpectedName(name, type, options);
138
139 if (expectedName !== filename) {
140 context.report(node, 'Filename must be "{{expectedName}}"', {
141 expectedName: expectedName
142 });
143 }
144 }
145 }
146 };
147 };
148 }())
149};