Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6eaed87
Require component name properties to be PascalCase
christianmemije Jul 13, 2018
26e0deb
Prettify
christianmemije Jul 13, 2018
179aa83
Rename core api components to use PascalCase
christianmemije Jul 16, 2018
745014b
Add component names to all components missing names
christianmemije Jul 16, 2018
6f8e257
Clean up root component names
christianmemije Jul 16, 2018
cfcce9e
Require vue components who are named index.vue to have parent dir mat…
christianmemije Jul 16, 2018
4c68e09
Rename core api component filenames
christianmemije Jul 16, 2018
8e17ffb
Rename core api component filenames
christianmemije Jul 16, 2018
554e723
Rename coach vue files
christianmemije Jul 17, 2018
8f0a009
Rename device vue files
christianmemije Jul 17, 2018
c91d5b0
Rename pdf vue filenames
christianmemije Jul 17, 2018
9ad83c0
Rename facility filenames
christianmemije Jul 17, 2018
5ac600f
Rename Learn vue filenames
christianmemije Jul 17, 2018
9dbf0ee
Rename Setup wizard vue filenames
christianmemije Jul 17, 2018
afd40f2
Rename Style guide vue filenames
christianmemije Jul 17, 2018
85abc9e
Rename User vue filenames
christianmemije Jul 17, 2018
701f3fa
Rename some directories
christianmemije Jul 17, 2018
757a102
Rename the rest of the files
christianmemije Jul 17, 2018
193cb88
Merge remote-tracking branch 'upstream/develop' into VueCasing
christianmemije Jul 17, 2018
ecceac6
Fix tests
christianmemije Jul 17, 2018
95f1a49
Make component registration PascalCase
christianmemije Jul 18, 2018
5eba45e
Change template component references to PascalCase
christianmemije Jul 18, 2018
9cfcc6b
Convert rules to plugin
christianmemije Jul 18, 2018
dae1633
Resolve merge conflict
christianmemije Jul 18, 2018
401f8b7
Fix eslint-plugin-kolibri pos in package.json
christianmemije Jul 18, 2018
1bae00a
Keep router-view kebab-case
christianmemije Jul 18, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 8 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ module.exports = {
'plugin:import/errors',
'plugin:import/warnings',
],
plugins: ['import', 'vue'],
plugins: ['import', 'vue', 'kolibri'],
settings: {
'import/resolver': {
[path.resolve(
Expand All @@ -64,7 +64,7 @@ module.exports = {
},
],
'vue/attribute-hyphenation': [2, 'never'],
'vue/name-property-casing': [0],
'vue/name-property-casing': [2, 'PascalCase'],
'vue/require-default-prop': 0,
'vue/html-self-closing': [
'error',
Expand Down Expand Up @@ -96,10 +96,16 @@ module.exports = {
},
],
'vue/html-closing-bracket-spacing': ['error'],
// Waiting on https://github.com/vuejs/eslint-plugin-vue/pull/397
// 'vue/component-name-in-template-casing': 3

'import/first': 1,
'import/no-duplicates': 1,
'import/newline-after-import': 1,
'import/order': 1,

// Custom vue rules
'kolibri/vue-filename-and-component-name-match': 2,
'kolibri/vue-component-registration-casing': 2,
},
};
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,6 @@ build_tools/crowdin-cli.jar

# Ignore pytest cache directory
.pytest_cache/

# Do not ignore this lib dir
!eslint-plugin-kolibri/**/lib/
2 changes: 1 addition & 1 deletion .htmlhintrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"tagname-lowercase": true,
"tagname-lowercase": false,

This comment was marked as spam.

"tag-pair": true,
"spec-char-escape": true,
"id-unique": true,
Expand Down
4 changes: 2 additions & 2 deletions docs/architecture/frontend_architecture.rst
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ The ``ContentRendererModule`` class has one required property ``getRendererCompo

.. code-block:: javascript

import contentRendererMixin from 'kolibri.coreVue.mixins.contentRenderer';
import contentRendererMixin from 'kolibri.coreVue.mixins.contentRendererMixin';

{
mixins: [contentRendererMixin],
Expand All @@ -145,7 +145,7 @@ The answer renderer should also define a ``checkAnswer`` method in its component

.. code-block:: javascript

import contentRendererMixin from 'kolibri.coreVue.mixins.contentRenderer';
import contentRendererMixin from 'kolibri.coreVue.mixins.contentRendererMixin';

{
mixins: [contentRendererMixin],
Expand Down
18 changes: 18 additions & 0 deletions eslint-plugin-kolibri/lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @fileoverview Custom rules.
* @author Learning Equality
*/
'use strict';

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

var requireIndex = require('requireindex');

//------------------------------------------------------------------------------
// Plugin Definition
//------------------------------------------------------------------------------

// import all rules in lib/rules
module.exports.rules = requireIndex(__dirname + '/rules');
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* @fileoverview Requires specific casing for component registration
*/

'use strict';

const utils = require('eslint-plugin-vue/lib/utils');
const casing = require('eslint-plugin-vue/lib/utils/casing');

const allowedCaseOptions = ['PascalCase', 'camelCase', 'snake_case'];

function create(context) {
const selectedCase = context.options[0];
const caseType =
allowedCaseOptions.indexOf(selectedCase) !== -1 ? selectedCase : allowedCaseOptions[0];
const converter = casing.getConverter(caseType);

return utils.executeOnVue(context, obj => {
const node = obj.properties.find(
p =>
p.type === 'Property' &&
p.key.type === 'Identifier' &&
p.key.name === 'components' &&
p.value.type === 'ObjectExpression'
);

if (!node) {
return;
}

const items = node.value.properties;
for (const item of items) {
const componentName = item.key.name;
const convertedName = converter(componentName);
if (convertedName !== componentName) {
context.report({
node: item,
message: 'Component "{{componentName}}" is not in {{caseType}}.',
data: {
componentName,
caseType,
},
});
}
}
});
}

module.exports = {
meta: {
docs: {
description: 'enforce specific casing for component registration',
category: undefined,
url: undefined,
},
fixable: undefined,
schema: [
{
enum: allowedCaseOptions,
},
],
},
create,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* @fileoverview Requires filename and component names for vue files to match
*/

'use strict';

const path = require('path');
const utils = require('eslint-plugin-vue/lib/utils');

function create(context) {
return utils.executeOnVue(context, obj => {
const filePath = context.getFilename();

// Skip if not .vue file
if (!filePath.endsWith('vue')) {
return;
}

const node = obj.properties.find(
item => item.type === 'Property' && item.key.name === 'name' && item.value.type === 'Literal'
);

// Components require a name
if (!node) {
context.report({
message: 'Component is missing a component name',
loc: {
start: {
line: 1,
column: 1,
},
end: {
line: 1,
column: 1,
},
},
});
return;
}

const componentName = node.value.value;

const fileName = path.basename(filePath, '.vue');
const parentDirName = path.basename(path.dirname(filePath));

// If index.vue, parent dir should match component name
if (fileName === 'index') {
if (componentName !== parentDirName) {
context.report({
node: node.value,
message:
'Parent dir name "{{parentDirName}}" does not match component name "{{componentName}}".',
data: {
parentDirName,
componentName,
},
});
}
return;
}

// Filename should match component name.
// Assumes component name is already PascalCase since we use vue/name-property-casing
if (componentName !== fileName) {
context.report({
node: node.value,
message: 'Filename "{{fileName}}" does not match component name {{componentName}}.',
data: {
fileName,
componentName,
},
});
}
});
}

module.exports = {
meta: {
docs: {
description: 'enforce filenames and component names to match',
category: undefined,
url: undefined,
},
fixable: undefined,
},
create,
};
21 changes: 21 additions & 0 deletions eslint-plugin-kolibri/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "eslint-plugin-kolibri",
"version": "0.0.1",
"description": "Custom rules.",
"author": "Learning Equality",
"main": "lib/index.js",
"scripts": {
"test": "mocha tests --recursive"
},
"dependencies": {
"requireindex": "^1.1.0"
},
"devDependencies": {
"eslint": "^3.9.1",
"mocha": "^3.1.2"
},
"engines": {
"node": ">=0.10.0"
},
"license": "MIT"
}
10 changes: 7 additions & 3 deletions frontend_build/src/extract_$trs.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ function isCamelCase(str) {
return /^[a-z][a-zA-Z0-9]*$/.test(str);
}

function isPascalCase(str) {
return /^[A-Z][a-z]+(?:[A-Z][a-z]+)*$/.test(str);
}

function generateMessagesObject(messagesObject) {
// define here and then let it be assigned during eval
var messages;
Expand Down Expand Up @@ -49,10 +53,10 @@ extract$trs.prototype.apply = function(compiler) {
if (nameSpaces.indexOf(messageNameSpace) !== -1) {
logging.error('Duplicate namespace ' + messageNameSpace + ' found in ' + module.resource);
} else if (Object.keys(messages).length) {
// Check that the namespace is camelCase.
if (!isCamelCase(messageNameSpace)) {
// Check that the namespace is PascalCase.
if (!isPascalCase(messageNameSpace)) {
logging.error(
`Name id "${messageNameSpace}" should be in camelCase. Found in ${module.resource}`
`Name id "${messageNameSpace}" should be in PascalCase. Found in ${module.resource}`
);
}
nameSpaces.push(messageNameSpace);
Expand Down
Loading